为什么我的结构寿命不够长?
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);
}
不明白为什么mystruct
不够活。如果我注释掉 mystruct.update()
行,它可以正常工作。更重要的是,如果我注释掉 update
的主体,代码仍然会失败。为什么调用借用可变 self
的空函数会改变事情?
我不明白错误所指的是哪个参考文献。有人可以解释一下吗?
此错误所涉及的引用是您调用 update()
时隐式创建的引用。因为update()
取&'a mut self
,也就是说它接受了一个&'a mut MyStruct<'a>
类型的值。这意味着理论上你应该这样调用 update()
:
(&mut mystruct).update();
如果到处都这样写会很不方便,因此 Rust 能够自动插入必要的 &
s、&mut
s 和 *
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()
调用不会导致编译错误,因为两个生命周期(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
}
}
这里我们有一个生命周期参数,'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>>
因为它可能会破坏以前存储的引用指向的元素,从而使代码内存不安全。
静态检查此类内容是不可能的,因此在借用检查级别上是不允许的。事实上,这只是一般借用检查规则的一个很好的结果。
在 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);
}
不明白为什么mystruct
不够活。如果我注释掉 mystruct.update()
行,它可以正常工作。更重要的是,如果我注释掉 update
的主体,代码仍然会失败。为什么调用借用可变 self
的空函数会改变事情?
我不明白错误所指的是哪个参考文献。有人可以解释一下吗?
此错误所涉及的引用是您调用 update()
时隐式创建的引用。因为update()
取&'a mut self
,也就是说它接受了一个&'a mut MyStruct<'a>
类型的值。这意味着理论上你应该这样调用 update()
:
(&mut mystruct).update();
如果到处都这样写会很不方便,因此 Rust 能够自动插入必要的 &
s、&mut
s 和 *
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()
调用不会导致编译错误,因为两个生命周期(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
}
}
这里我们有一个生命周期参数,'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>>
因为它可能会破坏以前存储的引用指向的元素,从而使代码内存不安全。
静态检查此类内容是不可能的,因此在借用检查级别上是不允许的。事实上,这只是一般借用检查规则的一个很好的结果。