首页 > 解决方案 > Lifetimes' handling when returning inner reference from a reference passed as an argument to the function

问题描述

What is the reason the following code compiles fine, despite both the lifetimes 'a and 'b being independent of each other?

struct Foo<'a> {
    i: &'a i32
}

fn func<'a, 'b>(x: &'a Foo<'b>) -> &'b i32 {
    x.i
}

fn main() {}

If I make the reference i in Foo mutable, it gives the following error.

5 | fn func<'a, 'b>(x: &'a Foo<'b>) -> &'b i32 {
  |                    -----------     -------
  |                    |
  |                    this parameter and the return type are declared with different lifetimes...
6 |     x.i
  |     ^^^ ...but data from `x` is returned here

What is the reason it gives the above error?. Does it consider it's ownership over mutable reference and it sees that something (from Foo) is being taken out (with an independent lifetime), which is not possible, hence the error ?

This code (which I thought would pass) fails too:

struct Foo<'a> {
    i: &'a mut i32
}

fn func<'a, 'b: 'a>(x: &'a Foo<'b>) -> &'b i32 {
    x.i
}

fn main() {}

fails with error:

 error[E0623]: lifetime mismatch
 --> src/main.rs:6:5
  |
5 | fn func<'a, 'b: 'a>(x: &'a Foo<'b>) -> &'b i32 {
  |                        -----------
  |                        |
  |                        these two types are declared with different lifetimes...
6 |     x.i
  |     ^^^ ...but data from `x` flows into `x` here

But this one passes:

struct Foo<'a> {
    i: &'a mut i32
}

fn func<'a: 'b, 'b>(x: &'a Foo<'b>) -> &'b i32 {
    x.i
}

fn main() {}

This seems a bit counter-intuitive to me. Here, the outer lifetime ('a) may outlive the inner lifetime ('b). Why is this not an error?

标签: referencerustlifetimemutable

解决方案


What is the reason the following code compiles fine, despite both the lifetimes 'a and 'b being independent of each other?

fn func<'a, 'b>(x: &'a Foo<'b>) -> &'b i32 {
    x.i
}

The reason is that they are not independent of each other.

The type &'a Foo<'b> would be impossible if 'a outlived 'b. So, implicitly the Rust borrow-checker is inferring that you must have intended that 'b: 'a ('b outlives 'a). So the code above is semantically the same as this:

fn func<'a, 'b: 'a>(x: &'a Foo<'b>) -> &'b i32 {
    x.i
}

If I make the reference i in Foo mutable, it gives the following error.

5 | fn func<'a, 'b>(x: &'a Foo<'b>) -> &'b i32 {
  |                    -----------     -------
  |                    |
  |                    this parameter and the return type are declared with different lifetimes...
6 |     x.i
  |     ^^^ ...but data from `x` is returned here

What is the reason it gives the above error?

When you pass around an immutable reference, it gets copied. In the above example, that means that the &'b i32 can be moved around by itself, and its liveness is not tied to where you got it from. This copied reference always points back to the original address of the data. And that's why the first example works - even if x was dropped, the original reference would still be valid.

When you pass around a mutable reference, it gets moved. A consequence of that is this case is that the reference is still "through" the variable x. If this wasn't the case, x's mutable reference to the contents of Foo could be live at the same time as this new immutable reference - which is not allowed. So, in this case the reference cannot outlive 'a - or put another way: 'a: 'b.

But didn't we already say 'b: 'a? The only conclusion here is that 'a and 'b must be the same lifetime, which is what your previous error message was demanding.


推荐阅读