rust - 为什么我的结构生命周期不够长?

标签 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 的空函数会改变一切?

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

最佳答案

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

(&mut mystruct).update();

到处都写这个会很不方便,所以 Rust 能够自动插入必要的 &小号,&mut s 和 * s 以便调用方法。这称为“自动引用”,它唯一发生的地方是方法调用/字段访问。

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

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

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

但是,当您有一个结构值并调用此方法时,应该已经有一个对 i32 的引用你存储在这个结构中。因此结构值的生命周期严格小于lifetime参数指定的生命周期,所以不可能构造&'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 ,它由函数的调用者 提供。例如,调用者可以使用静态引用调用此函数:

static X: i32 = 12345;

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

然而,当update()调用方法,mystruct生命周期受调用它的 block 的限制:

{
    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 的任何生命周期- 函数内的生命周期完全符合这些要求。因此,您可以根据您的值调用此类方法,编译器不会报错。

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

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

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

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

关于rust - 为什么我的结构生命周期不够长?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30435152/

相关文章:

rust - 在 Rust 中,如何限制泛型 T 以允许模数?

generics - 具有标记特征的Rust meta/复合结构

rust - 为什么 Rust 中的通用生命周期参数可以专门用于一个对象的两个不相交的生命周期?

rust - 从 Mutex 中同时借用引用和保护

rust - 如何修复 : cannot infer an appropriate lifetime for automatic coercion

rust - 对存储在向量中的值的引用的生命周期

reference - 从作为参数传递给函数的引用返回内部引用时的生命周期处理

android - 如何在 Android/iOS 上调试 Rust?

c++ - 自 C++20 以来,是否允许对分配的存储进行指针运算?

enums - 返回具有 'static tell the compiler this value doesn' t 的类型是否具有关联的生命周期,或者它是否使该值静态?