以为对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
