以为对rust的lifetime比较了解了,今天碰到一个诡异的问题,让我脑子出现了混乱,记录一下
struct Foo<'a> {
x: &'a u32
}
impl<'a> Foo<'a> {
fn get_x(&self) -> &'a u32 {
self.x
}
}
这是一个结构体,结构体有个 u32的 reference,’a 表示Foo不能outlive ref x,然后构造一个结构体,先从最简单的开始
fn main () {
let x_ref = &5;
let foo = Foo{
x: x_ref,
};
let x = foo.get_x();
println!("{}", x);
}
目前为止没有问题,接下来增加点复杂度
fn main() {
let x_ref = &5u32;
let y;
{
let foo = Foo {
x: x_ref,
};
y = foo.get_x();
}
println!("{}", y);
}
这里foo.get_x()也可以这样理解
let foo_ref: &'foo Foo<'a> = &foo;
Foo<'a>::get_x(foo_ref);
get_x() 扩展开就是
fn get_x(&self) -> &'a u32 {
self.x
}
impl<'a> Foo<'a> {
fn get_x<'foo>(self: &'foo Foo<'a>) -> &'a u32 {
}
}
这里有个隐藏关系,就是 ‘a: ‘foo lifetime ‘a 一定 outlive ‘foo,为什么?
fn main {
let x = &5;
let x_ref;
{
let f = Foo{
x: x;
};
x_ref = f.get_x();
}
println!("{}", x_ref);
}
如果x无法outlive foo, 那么,foo就会引用到悬空指针,反过来,foo可以提前销毁,但x任然被x_ref引用,所以,&'foo Foo<'a> 的隐藏含义就是'a:'foo, 这也是为什么rust不允许 &'static Foo<'a>, 因为没有一个'a可以outlive 'static
到这里为止也都容易理解,诡异的地方在下面
#[derive(Debug)]
struct Foo<'a> {
x_mut: &'a mut u32, // 把 &'a u32 改为 &'a mut u32
}
impl <'a> Foo<'a> {
fn get_x_mut(&mut self) -> &'a mut u32 {
self.x_mut
}
}
let x_ref = &mut 5u32;
let y;
{
let mut foo = Foo {
x_mut: x_ref,
};
y = foo.get_x_mut();
}
println!("{}", y);
如果这里把 &’a u32 改为 &’a mut u32, 编译就失败了
error[E0312]: lifetime of reference outlives lifetime of borrowed content...
--> src/myiter/mod.rs:43:9
|
43 | self.x_mut
| ^^^^^^^^^^
|
note: ...the reference is valid for the lifetime `'a` as defined here...
--> src/myiter/mod.rs:41:7
|
41 | impl <'a> Foo<'a> {
| ^^
note: ...but the borrowed content is only valid for the anonymous lifetime defined here
--> src/myiter/mod.rs:42:18
|
42 | fn get_x_mut(&self) -> &'a u32 {
| ^^^^^
把 get_x_mut 展开
fn get_x_mut<'foo>(self: &'foo mut Foo<'a>) -> &'a mut u32 {
self.x_mut
}
error[E0312]: lifetime of reference outlives lifetime of borrowed content...
--> src/myiter/mod.rs:43:9
|
43 | self.x_mut
| ^^^^^^^^^^
|
note: ...the reference is valid for the lifetime `'a` as defined here...
--> src/myiter/mod.rs:41:7
|
41 | impl <'a> Foo<'a> {
| ^^
note: ...but the borrowed content is only valid for the lifetime `'foo` as defined here
--> src/myiter/mod.rs:42:18
|
42 | fn get_x_mut<'foo>(&'foo self) -> &'a u32 {
| ^^^^
这个错告诉我们 lifetime of reference outlives lifetime of borrowed content… 意思是 x_mut的lifetime ‘a outlive 了 ‘foo, ‘foo 代表了self也就是 对Foo引用的lifetime。这个错误很奇怪啊,刚才不是说 ‘a 就是要 outlive ‘foo吗,怎么这会又报错呢?这里一定和mut
有关,如果是mut refenece, Foo的lifetime必须 outlive ‘a, 这和之前share ref是相反的。
这和liftime subtyping有关,因为 mutable reference is invariant in their argument type
, 因为这里self是隐式的第一个参数,所以’foo必须outlive ‘a
fn get_x_mut<'foo>(self: &'foo mut Foo<'a>) -> &'a mut u32
当self是mut时,编译器认为你会修改self,或者self中的reference,这里是’a, 如果’a outlive ‘foo,那么有可能foo给 ‘a的reference指向一个引用当 foo drop时候,x_mut 可能指向的是一个dangling ref
#[derive(Debug)]
struct Foo<'a> {
x_mut: &'a mut u32,
owned: u32,
}
fn get_x_mut<'foo>(self: &'foo mut Foo<'a>) -> &'a mut u32{
self.x_mut = &mut self.owned;
// self.x_mut = v;
self.x_mut
}
这个时候你需要告诉compiler,'foo outlive 'a
,这样就可以了
fn get_x_mut<'foo:'a>(self: &'foo mut Foo<'a>) -> &'a mut u32{
self.x_mut = &mut self.owned;
// self.x_mut = v;
self.x_mut
}
what I have learnt
- lifetime subtyping https://doc.rust-lang.org/nomicon/subtyping.html
- struct lifetime