在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
Run Code Online (Sandbox Code Playgroud)
对于以下代码:
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);
}
Run Code Online (Sandbox Code Playgroud)
我不明白为什么mystruct活不够.如果我注释掉它的mystruct.update()行,它可以正常工作.更重要的是,如果我注释掉update代码的主体仍然失败.为什么调用一个借用可变变量的空函数会self改变一些东西?
我不明白哪个参考是错误所涉及的那个.有人可以解释一下吗?
此错误所引用的引用是在您调用时隐式创建的引用update().因为update()take &'a mut self,它意味着它接受一个类型的值&'a mut MyStruct<'a>.这意味着理论上你应该这样调用update():
(&mut mystruct).update();
Run Code Online (Sandbox Code Playgroud)
在任何地方编写它都会非常不方便,因此Rust能够自动插入必要的&s,&muts和*s以调用方法.这称为"自动引用",它发生的唯一地方是方法调用/字段访问.
问题是update()方法的定义:
impl<'a> MyStruct<'a> {
...
fn update(&'a mut self) { ... }
...
}
Run Code Online (Sandbox Code Playgroud)
在这里,您要求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
Run Code Online (Sandbox Code Playgroud)
这样,此方法调用中结构的生命周期与此结构包含的引用无关,并且可以更小.
下面将进行更深入的解释.
你自己的定义本身并不是无稽之谈.例如:
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();
Run Code Online (Sandbox Code Playgroud)
这里的update()调用不会导致编译错误,因为两个生命周期(包含在其中Y的X,引用的生命周期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
}
}
Run Code Online (Sandbox Code Playgroud)
这里我们有一个生命周期参数,'a由函数的调用者提供.例如,调用者可以使用静态引用调用此函数:
static X: i32 = 12345;
MyStruct::new(&X); // here &X has static lifetime
Run Code Online (Sandbox Code Playgroud)
但是,当update()调用方法时,mystruct生命周期受到调用它的块的限制:
{
let initial = vec![Box::new(1), Box::new(2)];
let mystruct = MyStruct { v : initial, p : &arg }; // +
// |
mystruct.update(); // |
// |
mystruct // |
}
Run Code Online (Sandbox Code Playgroud)
当然,借用检查器无法证明此生命周期与调用者提供的生命周期相同(并且对于任何可能的"外部"生命周期,它们确实不可能匹配),因此它会引发错误.
更新定义如下:
fn update(&mut self) { ... }
// or, equivalently
fn update<'b>(&'b mut self) where 'a: 'b { ... }
Run Code Online (Sandbox Code Playgroud)
然后当你调用它时,不再要求你调用这个方法的值必须完全和'a它一样长- 它足以让它适用于任何小于或等于'a的生命周期 - 以及函数内部的生命周期完全符合这些要求.因此,您可以在您的值上调用此类方法,编译器不会抱怨.
另外(正如评论中所注意到的)以下行确实无效,并且没有办法解决它:
self.p = &self.v.last().unwrap();
Run Code Online (Sandbox Code Playgroud)
借用检查在这里失败,因为您试图将具有结构生命周期的引用存储到结构本身中.一般来说,这是不可能的,因为它有令人讨厌的健全性问题.例如,假设您确实能够将此引用存储到结构中.但是现在你不能Vec<Box<i32>>在结构中变异,因为它可能会破坏先前存储的引用所指向的元素,从而使代码内存不安全.
静态检查这些东西是不可能的,因此在借用检查级别上是不允许的.事实上,这只是一般借款检查规则的一个很好的结果.