The following code
struct Cat<'a, T> {
coolness: &'a T,
}
complains saying
error[E0309]: the parameter type `T` may not live long enough
--> src/main.rs:2:5
|
1 | struct Cat<'a, T> {
| - help: consider adding an explicit lifetime bound `T: 'a`...
2 | coolness: &'a T,
| ^^^^^^^^^^^^^^^
|
note: ...so that the reference type `&'a T` does not outlive the data it points at
--> src/main.rs:2:5
|
2 | coolness: &'a T,
| ^^^^^^^^^^^^^^^
With an explicit lifetime bound, it compiles. When I instantiate the struct where T
is an &i32
and despite each reference having a different lifetime, the code compiles. My understanding is that the compiler sees that the inner &
outlives the outer &
:
struct Cat<'a, T>
where
T: 'a,
{
coolness: &'a T,
}
fn main() {
let b = 10;
{
let c = &b;
{
let fluffy = Cat { coolness: &c };
}
}
}
Does Cat { coolness: &c }
expand to Cat { coolness: &'a &'a i32 }
? Does the inner reference also assume the same lifetime and so forth for more nested references?
Does
Cat { coolness: &c }
expand toCat { coolness: &'a &'a i32 }
?
Yes, the Cat
ends up with a reference to a reference. This can be demonstrated by the following code compiling:
let fluffy = Cat { coolness: &c };
fn is_it_a_double_ref(_x: &Cat<&i32>) {}
is_it_a_double_ref(&fluffy);
However, the lifetime on each reference is not necessarily the same.
My understanding is that the compiler sees that the inner & outlives the outer &
That's right. And this is precisely where the T: 'a
bound comes into play.
Lifetime bounds are a bit tricky to understand at first. They put restrictions on the references contained in T
. For example, given the bound T: 'static
, types that don't contain any references, or only contain 'static
references, e.g. i32
and &'static str
, satisfy the bound, while types that contain non-'static
references, e.g. &'a i32
, don't, because 'a: 'static
is false. More generally, given the bound T: 'a
, the type T
satisfies the bound if, for every lifetime parameter 'x
on T
, 'x: 'a
is true (types with no lifetime parameters trivially satisfy the bound).
Back to your code now. Let's give some names to these references. We'll say the type of coolness
is &'fluffy &'c i32
. 'c
is the lifetime of the variable c
and 'fluffy
is the lifetime of the variable fluffy
(counterintuitively, lifetimes encode the scope of a borrow, not the lifetime of the referent, although the compiler does check that the borrow doesn't extend beyond the referent's lifetime). That means the type of Fluffy
is Cat<'fluffy, &'c i32>
. Is it true that &'c i32: 'fluffy
?
To check if &'c i32: 'fluffy
is true, we need to check if 'c: 'fluffy
is true. 'c: 'fluffy
is true because the variable c
goes out of scope after fluffy
.