为什么我的结构寿命不足够长? [英] Why doesn't my struct live long enough?

查看:76
本文介绍了为什么我的结构寿命不足够长?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Rust中,出现以下错误:

In Rust, I get the following error:

<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);
}

(游乐场)

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

I don't understand why mystruct does not live enough. If I comment out the mystruct.update() line it works fine though. What's more is, if I comment out the body of update the code still fails. Why does calling an empty function which borrows a mutable self changes things?

我不知道该错误所涉及的是哪一个参考.有人可以解释吗?

I don't understand which reference is the one the error talks about. Can somebody explain this?

推荐答案

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

The reference this error talks about is the one which is implicitly created when you call update(). Because update() takes &'a mut self, it means that it accepts a value of type &'a mut MyStruct<'a>. It means that in theory you should call update() like this:

(&mut mystruct).update();

在任何地方编写它都会很不方便,因此Rust能够自动插入必要的& s,&mut s和* s来调用方法.这称为自动引用",它发生的唯一位置是方法调用/字段访问.

It would be very inconvenient to write this everywhere, and so Rust is able to automatically insert necessary &s, &muts and *s in order to call a method. This is called "autoreference", and the only place it happens is method invocations/field access.

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

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

在这里,您要求update()通过寿命为'a的引用接收调用它的值,其中'a是存储在结构中的引用的寿命.

Here you are requesting that update() receives the value it is called at via a reference with lifetime 'a, where 'a is the lifetime of the reference stored in the structure.

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

However, when you have a structure value you're calling this method on, there should be already a reference to i32 you stored in this structure. Hence the lifetime of the structure value is strictly smaller than the lifetime designated by the lifetime parameter, so it is just impossible to construct &'a mut MyStruct<'a> with local variables (as in your case).

解决方案是使用&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

通过这种方法,该方法调用中的结构寿命不会与该结构包含的引用绑定,并且可以更短.

This way the lifetime of the structure in this method call is not tied to the reference this structure contains and can be smaller.

以下是更深入的说明.

您自己的定义不是胡说八道.例如:

By itself your definition is not nonsense. For example:

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.

Here update() invocation won't cause compilation errors because both lifetimes (of Y and of X, reference to which is contained in Y) are 'static.

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

Let's consider your example, for comparison:

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,由该函数的调用方提供.例如,调用者可以使用静态引用来调用此函数:

Here we have a lifetime parameter, 'a, which is supplied by the caller of the function. For example, the caller could call this function with a static reference:

static X: i32 = 12345;

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

但是,当调用update()方法时,mystruct生存期受在以下方法中调用的块的限制:

However, when update() method is invoked, mystruct lifetime is bounded by the block it is called in:

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

自然地,借用检查器无法证明此生存期与调用方提供的生存期相同(并且对于任何可能的外部"生存期,它们实际上是不可能匹配的),因此将引发错误.

Naturally, the borrow checker can't prove that this lifetime is the same as the lifetime provided by the caller (and for any possible "external" lifetime it is indeed impossible for them to match), so it throws an error.

当这样定义更新时:

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

然后,当您调用它时,不再要求调用此方法的值必须与'a完全一样长-足以使其生存期小于或等于'a-并且函数内部的生存期完全符合这些要求.因此,您可以根据自己的值调用此类方法,并且编译器不会抱怨.

then when you call it, it is no longer required that the value you call this method on must live exactly as long as 'a - it is sufficient for it to live for any lifetime which is smaller than or equal to 'a - and the lifetime inside the function perfectly matches these requirements. Thus you can call such method on your value, and the compiler won't complain.

此外(如注释所示),以下行的确是无效的,并且无法解决:

Additionally (as noticed in the comments) the following line is indeed invalid and there is no way around it:

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

这里的借位检查失败,因为您试图将具有结构寿命的引用存储到结构本身中.通常,无法完成此操作,因为它存在令人讨厌的健全性问题.例如,假设您确实能够将此引用存储到结构中.但是,现在您不能在结构中进行突变,因为它可能破坏先前存储的引用所指向的元素,从而使代码存储器不安全.

The borrow check fails here because you're trying to store a reference with lifetime of the structure into the structure itself. In general this can't be done because it has nasty soundness issues. For example, suppose you were indeed able to store this reference into the structure. But now you can't mutate Vec<Box<i32>> in the structure because it may destroy an element which the previously stored references points at, making the code memory unsafe.

不可能静态地检查这些东西,因此在借阅检查级别是不允许的.实际上,这只是一般借阅检查规则的一个很好的结果.

It is impossible to check for such things statically, and so it is disallowed on the borrow checking level. In fact, it is just a nice consequence of general borrow checking rules.

这篇关于为什么我的结构寿命不足够长?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆