以为对rust的lifetime比较了解了,今天碰到一个诡异的问题,让我脑子出现了混乱,记录一下

  1. struct Foo<'a> {
  2. x: &'a u32
  3. }
  4. impl<'a> Foo<'a> {
  5. fn get_x(&self) -> &'a u32 {
  6. self.x
  7. }
  8. }

这是一个结构体,结构体有个 u32的 reference,’a 表示Foo不能outlive ref x,然后构造一个结构体,先从最简单的开始

  1. fn main () {
  2. let x_ref = &5;
  3. let foo = Foo{
  4. x: x_ref,
  5. };
  6. let x = foo.get_x();
  7. println!("{}", x);
  8. }

目前为止没有问题,接下来增加点复杂度

  1. fn main() {
  2. let x_ref = &5u32;
  3. let y;
  4. {
  5. let foo = Foo {
  6. x: x_ref,
  7. };
  8. y = foo.get_x();
  9. }
  10. println!("{}", y);
  11. }

这里foo.get_x()也可以这样理解

  1. let foo_ref: &'foo Foo<'a> = &foo;
  2. Foo<'a>::get_x(foo_ref);

get_x() 扩展开就是

  1. fn get_x(&self) -> &'a u32 {
  2. self.x
  3. }
  4. impl<'a> Foo<'a> {
  5. fn get_x<'foo>(self: &'foo Foo<'a>) -> &'a u32 {
  6. }
  7. }

这里有个隐藏关系,就是 ‘a: ‘foo lifetime ‘a 一定 outlive ‘foo,为什么?

  1. fn main {
  2. let x = &5;
  3. let x_ref;
  4. {
  5. let f = Foo{
  6. x: x;
  7. };
  8. x_ref = f.get_x();
  9. }
  10. println!("{}", x_ref);
  11. }
  12. 如果x无法outlive foo 那么,foo就会引用到悬空指针,反过来,foo可以提前销毁,但x任然被x_ref引用,所以,&'foo Foo<'a> 的隐藏含义就是'a:'foo, 这也是为什么rust不允许 &'static Foo<'a>, 因为没有一个'a可以outlive 'static

到这里为止也都容易理解,诡异的地方在下面

  1. #[derive(Debug)]
  2. struct Foo<'a> {
  3. x_mut: &'a mut u32, // 把 &'a u32 改为 &'a mut u32
  4. }
  5. impl <'a> Foo<'a> {
  6. fn get_x_mut(&mut self) -> &'a mut u32 {
  7. self.x_mut
  8. }
  9. }
  10. let x_ref = &mut 5u32;
  11. let y;
  12. {
  13. let mut foo = Foo {
  14. x_mut: x_ref,
  15. };
  16. y = foo.get_x_mut();
  17. }
  18. println!("{}", y);

如果这里把 &’a u32 改为 &’a mut u32, 编译就失败了

  1. error[E0312]: lifetime of reference outlives lifetime of borrowed content...
  2. --> src/myiter/mod.rs:43:9
  3. |
  4. 43 | self.x_mut
  5. | ^^^^^^^^^^
  6. |
  7. note: ...the reference is valid for the lifetime `'a` as defined here...
  8. --> src/myiter/mod.rs:41:7
  9. |
  10. 41 | impl <'a> Foo<'a> {
  11. | ^^
  12. note: ...but the borrowed content is only valid for the anonymous lifetime defined here
  13. --> src/myiter/mod.rs:42:18
  14. |
  15. 42 | fn get_x_mut(&self) -> &'a u32 {
  16. | ^^^^^

把 get_x_mut 展开

  1. fn get_x_mut<'foo>(self: &'foo mut Foo<'a>) -> &'a mut u32 {
  2. self.x_mut
  3. }
  1. error[E0312]: lifetime of reference outlives lifetime of borrowed content...
  2. --> src/myiter/mod.rs:43:9
  3. |
  4. 43 | self.x_mut
  5. | ^^^^^^^^^^
  6. |
  7. note: ...the reference is valid for the lifetime `'a` as defined here...
  8. --> src/myiter/mod.rs:41:7
  9. |
  10. 41 | impl <'a> Foo<'a> {
  11. | ^^
  12. note: ...but the borrowed content is only valid for the lifetime `'foo` as defined here
  13. --> src/myiter/mod.rs:42:18
  14. |
  15. 42 | fn get_x_mut<'foo>(&'foo self) -> &'a u32 {
  16. | ^^^^

这个错告诉我们 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

  1. 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

  1. #[derive(Debug)]
  2. struct Foo<'a> {
  3. x_mut: &'a mut u32,
  4. owned: u32,
  5. }
  6. fn get_x_mut<'foo>(self: &'foo mut Foo<'a>) -> &'a mut u32{
  7. self.x_mut = &mut self.owned;
  8. // self.x_mut = v;
  9. self.x_mut
  10. }

这个时候你需要告诉compiler,'foo outlive 'a ,这样就可以了

  1. fn get_x_mut<'foo:'a>(self: &'foo mut Foo<'a>) -> &'a mut u32{
  2. self.x_mut = &mut self.owned;
  3. // self.x_mut = v;
  4. self.x_mut
  5. }

what I have learnt

  1. lifetime subtyping https://doc.rust-lang.org/nomicon/subtyping.html
  2. struct lifetime