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

时间:2015-05-25 09:30:44

标签: rust lifetime

在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的空函数会改变一些东西?

我不明白哪个引用是错误所涉及的引用。有人可以解释一下吗?

1 个答案:

答案 0 :(得分:8)

此错误所引用的引用是在您调用update()时隐式创建的引用。由于update()需要&'a mut self,因此它表示它接受&'a mut MyStruct<'a>类型的值。这意味着理论上你应该像这样调用update()

(&mut mystruct).update();

在任何地方写这个都非常不方便,因此Rust能够自动插入必要的& s,&mut*以调用方法。这被称为&#34; autoreference&#34;,它发生的唯一地方是方法调用/字段访问。

问题是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()调用不会导致编译错误,因为Y和[{1}}的生命周期(X中包含的引用){ {1}}。

让我们考虑你的例子,进行比较:

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方法时,static X: i32 = 12345; MyStruct::new(&X); // here &X has static lifetime 生命周期受到调用它的块的限制:

update()

当然,借阅检查员无法证明此生命周期与来电者提供的生命周期相同(对于任何可能的&#34;外部&#34;生命周期,他们确实无法匹配) ,所以它会引发错误。

当更新定义如下:

mystruct

然后当你调用它时,不再需要你调用此方法的值必须与{ let initial = vec![Box::new(1), Box::new(2)]; let mystruct = MyStruct { v : initial, p : &arg }; // + // | mystruct.update(); // | // | mystruct // | } 一样长 - 它足以让它在任何小于或等于的生命周期中存活到fn update(&mut self) { ... } // or, equivalently fn update<'b>(&'b mut self) where 'a: 'b { ... } - 函数内部的生命周期完全符合这些要求。因此,您可以在您的值上调用此类方法,编译器不会抱怨。

另外(正如评论中所注意到的)以下行确实无效,并且无法绕过它:

'a

借用检查在此处失败,因为您试图将具有结构生命周期的引用存储到结构本身中。一般来说,这是不可能的,因为它有令人讨厌的健全性问题。例如,假设您确实能够将此引用存储到结构中。但是现在你不能在结构中变异'a,因为它可能会破坏先前存储的引用所指向的元素,从而使代码内存不安全。

无法静态检查这些内容,因此在借用检查级别上不允许这样做。事实上,这只是一般借款检查规则的一个很好的结果。