简体   繁体   English

Rust:生命周期使用不当

[英]Rust: Bad use of lifetime

I tested a small example with lifetimes but sometimes i can't understand the problem, like here:我用生命周期测试了一个小例子,但有时我无法理解这个问题,就像这里:

struct Space<'r> {
    v: &'r mut usize,
    z: usize,
}

impl<'r> Space<'r> {
    fn new(v: &'r mut usize) -> Self {
        Self { v, z: 0 }
    }
    
    fn add(&'r mut self) {
        self.z += 1;
        *self.v = self.z
    }

    fn make_val(&'r mut self, v: u32) -> Val<'r> {
        Val::new(self, v)
    }
}

struct Val<'r> {
    space: &'r mut Space<'r>,
    v: u32,
}

impl<'r> Val<'r> {
    fn new(space: &'r mut Space<'r>, v: u32) -> Self {
        Self { space, v }
    }
}

impl<'r> Clone for Val<'r> {
    fn clone(&self) -> Self {
        self.space.add();
        
        Self { space: self.space, v: self.v }
    }
}

fn main() {
    let mut v = 0usize;
    let mut space = Space::new(&mut v);
    let mut val1 = space.make_val(1234);
    let mut val2 = val1.clone();
    
    println!("Res: {} - {}/{}", v, val1.v, val2.v);
}

( Playground ) 游乐场

There is 2 compilation errors, the error at line 36 is difficult to fix for me but the first one at line 34 is really strange, it say that the lifetime cannot outlive be the method call is internal, nothing go outside.有 2 个编译错误,第 36 行的错误对我来说很难修复,但第 34 行的第一个错误真的很奇怪,它说生命周期不能超过,因为方法调用是内部的,什么都没有。

Can you explain me theses problem and how to solve them, please ?你能解释一下这些问题以及如何解决它们吗?

Going through the parts that stand out to me, here are the potential changes I would make.通过对我来说突出的部分,以下是我可能会做出的改变。

Structs结构

Going through the two structs you have, the first one looks reasonable.通过您拥有的两个结构,第一个看起来很合理。

struct Space<'r> {
    v: &'r mut usize,
    z: usize,
}

However, the Val struct doesn't make as much sense to me.但是, Val结构对我来说没有多大意义。 If the reference to Space has the same lifetime 'r as the Space itself, then it is essentially equivalent to moving Val<'r> into Space<'r> .如果参照Space具有相同的寿命'rSpace本身,那么它是本质上等价于移动Val<'r>Space<'r> While not illegal, I would recommend changing it.虽然不违法,但我建议更改它。

// Just claim Space<'r> completely
struct Val<'r> {
    space: Space<'r>,
    v: u32,
}

Another option would be to add an additional lifetime to Val<'r> so that make_val could be called more than once without causing lifetime conflicts.另一种选择是为Val<'r>添加一个额外的生命周期,这样make_val可以被多次调用而不会导致生命周期冲突。

// life of reference to space 'a <= 'r
struct Val<'a, 'r: 'a> {
    space: &'a mut Space<'r>,
    v: u32,
}

// Modify to allow for second lifetime to be used.
fn make_val<'a>(&'a mut self, v: u32) -> Val<'a, 'r> {
    Val { space: self, v }
}

Space::add

The explicit lifetime on &'r mut self is not necessary for the function to run. &'r mut self上的显式生命周期对于函数运行来说不是必需的。 Of the two errors you get when attempting to run your code, the entire first error is just the compiler telling you why this won't work.在尝试运行代码时遇到的两个错误中,整个第一个错误只是编译器告诉您为什么这不起作用。 It is over constraining the function and unnecessarily confusing the compiler so lets remove it.它过度限制了函数并不必要地混淆了编译器,因此让我们将其删除。

fn add(&mut self) {
    self.z += 1;
    *self.v = self.z
}

The reason it was unable to resolve it was because &mut self is really two lifetimes that you are requiring be equal.它无法解决它的原因是因为&mut self实际上是您要求平等的两个生命周期。 It may make more sense if we visualize all the lifetimes involved:如果我们可视化所有涉及的生命周期,可能会更有意义:

// add(&mut self) tells the compiler to infer a lifetime for 'a that doesn't exceed 'b
fn add<'a, 'b: 'a>(self: &'a mut Space<'b>) {}

// add(&'r mut self) tells the compiler that the reference must live as long as the data 
fn add<'b>(self: &'b mut Space<'b>) {}

Cloning克隆

Cloning something containing a mutable reference doesn't make sense.克隆包含可变引用的内容没有意义。 Rust very explicitly forbids having multiple mutable references to something at any given point in memory. Rust 非常明确地禁止在内存中的任何给定点有多个可变引用。 Cloning the reference is technically possible, but would require unsafe code.克隆引用在技术上是可行的,但需要不安全的代码。 You shouldn't ever do this, but I wrote it anyway below.你永远不应该这样做,但我还是在下面写了它。

impl<'r> Clone for Val<'r> {
    fn clone(&self) -> Val<'r> {
        unsafe {
            // Make self mutable so we can access self.space
            let my_self: &mut Self = &mut *(self as *const _ as *mut _);

            // Requires a mutable reference to self to run space::add
            my_self.space.add();
        
            Self {
                // Duplicate reference via unsafe code
                space: &mut *my_self.space,
                v: self.v
            }
        }
    }
}

Printing印刷

Finally, the print line in main uses v despite a mutable reference to it still being in use.最后,main 中的打印行使用v尽管对它的可变引用仍在使用中。 This is fairly self explanatory so I won't go into much more detail.这是相当不言自明的,所以我不会详细介绍。

Safe Rust安全防锈

The safe way to solve this problem is to use Rc<Refcell<T>> .解决这个问题的安全方法是使用Rc<Refcell<T>> The Rc allows for multiple owned references to be taken across a single thread and the RefCell allows interior mutability. Rc允许跨单个线程获取多个拥有的引用,而RefCell允许内部可变性。 RefCell is able to do this by checking if any other references exist at runtime instead of compile time. RefCell能够通过检查在运行时而不是编译时是否存在任何其他引用来做到这一点。 Here is a version of the playground that works with a single RefCell , but a second could also be used to better match your original code.这是一个使用单个RefCell操场版本,但也可以使用第二个来更好地匹配您的原始代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM