为什么我的结构寿命不够长?

Why doesn't my struct live long enough?

在 Rust 中,我收到以下错误:

<anon>:14:9: 14:17 error: `mystruct` does not live long enough
<anon>:14         mystruct.update();
                  ^~~~~~~~
<anon>:10:5: 17:6 note: reference must be valid for the lifetime 'a as defined on the block at 10:4...
<anon>:10     {
<anon>:11         let initial = vec![Box::new(1), Box::new(2)];
<anon>:12         let mystruct = MyStruct { v : initial, p : &arg };
<anon>:13         
<anon>:14         mystruct.update();
<anon>:15         
          ...
<anon>:12:59: 17:6 note: ...but borrowed value is only valid for the block suffix following statement 1 at 12:58
<anon>:12         let mystruct = MyStruct { v : initial, p : &arg };
<anon>:13         
<anon>:14         mystruct.update();
<anon>:15         
<anon>:16         mystruct
<anon>:17     }
error: aborting due to previous error

对于以下代码:

struct MyStruct<'a>
{
    v : Vec<Box<i32>>,
    p : &'a i32
}

impl<'a> MyStruct<'a>
{
    fn new(arg : &'a i32) -> MyStruct<'a>
    {
        let initial = vec![Box::new(1), Box::new(2)];
        let mystruct = MyStruct { v : initial, p : &arg };

        mystruct.update();

        mystruct
    }

    fn update(&'a mut self)
    {
        self.p = &self.v.last().unwrap();
    }

}

fn main() {
    let x = 5;
    let mut obj = MyStruct::new(&x);
}

(Playground)

不明白为什么mystruct不够活。如果我注释掉 mystruct.update() 行,它可以正常工作。更重要的是,如果我注释掉 update 的主体,代码仍然会失败。为什么调用借用可变 self 的空函数会改变事情?

我不明白错误所指的是哪个参考文献。有人可以解释一下吗?

此错误所涉及的引用是您调用 update() 时隐式创建的引用。因为update()&'a mut self,也就是说它接受了一个&'a mut MyStruct<'a>类型的值。这意味着理论上你应该这样调用 update()

(&mut mystruct).update();

如果到处都这样写会很不方便,因此 Rust 能够自动插入必要的 &s、&muts 和 *s 以调用方法.这称为 "autoreference",它唯一发生的地方是方法 invocations/field 访问。

问题是update()方法的定义:

impl<'a> MyStruct<'a> {
    ...
    fn update(&'a mut self) { ... }
    ...
}

此处您请求 update() 通过生命周期为 'a 的引用接收调用它的值,其中 'a 是存储在结构中的引用的生命周期。

但是,当您有一个结构值并调用此方法时,应该已经有对存储在该结构中的 i32 的引用。因此,结构值的生命周期严格小于生命周期参数指定的生命周期,因此不可能用局部变量构造 &'a mut MyStruct<'a>(如您的情况)。

解决办法是用&mut self代替&'a mut self:

fn update(&mut self) { ... }
// essentially equivalent to
fn update<'b>(&'b mut self) where 'a: 'b { ... }
// `'b` is a fresh local lifetime parameter

这样,此方法调用中的结构的生命周期与此结构包含的引用无关,并且可以更短。

更深入的解释如下。

你的定义本身并不是废话。例如:

struct IntRefWrapper<'a> {
    value: &'a i32
}

static X: i32 = 12345;
static Y: IntRefWrapper<'static> = IntRefWrapper { value: &X };

impl<'a> IntRefWrapper<'a> {
    fn update(&'a self) { ... }
}

Y.update();

此处 update() 调用不会导致编译错误,因为两个生命周期(YX 的生命周期,Y 中包含对其的引用)是 'static.

让我们考虑您的示例,以进行比较:

impl<'a> MyStruct<'a> {
    fn new(arg : &'a i32) -> MyStruct<'a> {
        let initial = vec![Box::new(1), Box::new(2)];
        let mystruct = MyStruct { v : initial, p : &arg };

        mystruct.update();

        mystruct
    }
}

这里我们有一个生命周期参数,'a,它由函数的 caller 提供。例如,调用者可以使用静态引用调用此函数:

static X: i32 = 12345;

MyStruct::new(&X);  // here &X has static lifetime

但是,当调用 update() 方法时,mystruct 生命周期受调用它的块的限制:

{
    let initial = vec![Box::new(1), Box::new(2)];
    let mystruct = MyStruct { v : initial, p : &arg };  // +
                                                        // |
    mystruct.update();                                  // |
                                                        // |
    mystruct                                            // |
}

自然,借用检查器无法证明此生命周期与调用者提供的生命周期相同(并且对于任何可能的 "external" 生命周期,它们确实不可能匹配),因此它抛出错误。

当这样定义更新时:

fn update(&mut self) { ... }
// or, equivalently
fn update<'b>(&'b mut self) where 'a: 'b { ... }

然后当你调用它时,不再要求你调用此方法的值必须与 'a 一样长 - 它足以在小于任何生命周期的情况下存活或等于 'a - 函数内的生命周期完全符合这些要求。因此你可以在你的值上调用这样的方法,编译器不会报错。

此外(正如评论中所指出的)以下行确实无效,没有办法解决它:

self.p = &self.v.last().unwrap();

此处借用检查失败,因为您试图将具有结构生命周期的引用存储到结构本身中。一般来说,这是无法完成的,因为它存在严重的稳健性问题。例如,假设您确实能够将此引用存储到结构中。但是现在你不能在结构中改变 Vec<Box<i32>> 因为它可能会破坏以前存储的引用指向的元素,从而使代码内存不安全。

静态检查此类内容是不可能的,因此在借用检查级别上是不允许的。事实上,这只是一般借用检查规则的一个很好的结果。