Reputation: 7561
Why does this code compile?
#[derive(Debug)]
pub struct Foo<'a> {
pub x: &'a [&'a str],
}
fn main() {
let s = "s".to_string();
let t: &str = s.as_ref();
{
let v = vec![t];
let foo = Foo { x: &v[..] };
println!("{:?}", &foo);
}
}
In my Foo
struct, my x
field contains a slice of &strs
. My understanding of these lifetime labels is that the slice and the individual &strs
have the same lifetime.
However, in my example the &str
t
(in the outer block) does not have the same lifetime as the container slice (in the inner block).
Upvotes: 1
Views: 277
Reputation: 300099
The syntax 'a
is actually used in two different situations:
The first situation, labeling a loop:
fn main() {
'a: loop {
println!("{}", 3);
break 'a;
}
}
Here, 'a
clearly delineates the lifetime of the loop body, and allows breaking from multiple layers of loops in one fell swoop.
The second, and more similar situation, is using 'a
to represent a bound:
fn<'a> susbtr(haystack: &'a str, offset: usize) -> &'a str;
In this case, the lifetime 'a
does not represent the actual lifetime of the variable, it represents a bound on the lifetime of the referenced variable and allows tying together the bounds of various variables.
Note that the caller and callee interpret the bound differently:
'a
is an upper-bound, a promise that the return value will live at least as long as the parameter (maybe longer, no guarantee)substr
), 'a
is a lower-bound, a check that any returned value must live at least as long as the parameter (maybe longer, not necessary)We can have variance since the bound does not represent the actual lifetime, when a single bound is used for multiple lifetimes, the compiler will simply deduce the lowest/highest bound that makes sense for the situation:
For example:
fn<'b> either(one: &'b str, two: &'b str, flag: bool) -> &'b str {
if flag { one } else { two }
}
can be called with:
fn<'a> call(o: &'a str, flag: bool) -> &'a str {
either(o, "Hello, World", flag)
}
Here, the lifetime of o
is unknown (some 'a
) whilst the lifetime of "Hello, World"
is known ('static
), 'static
is by definition the greater of the lifetimes (it lives for all the program).
call
only knows that the return value lives at least as long as o
call
must guarantee this, it supplies o
and "Hello, World"
to either
where 'b
is deduced to be the lowest bound between 'a
and 'static
(thus 'a
)either
simply must return something that lives as long as either one of its arguments; it's unaware that their lifetime may differ, and cares notUpvotes: 3
Reputation: 431599
My understanding of these lifetime labels is that the slice and the individual
&strs
have the same lifetime.
This is a common misconception. It really means that there has to be a lifetime that applies to both. When instantiated, the Foo
struct's 'a
corresponds to the lines of the inner block after let v = vec![t];
as that is a lifetime that both variables share.
If this flexibility didn't exist, lifetimes would be super painful to use. Variables defined on two lines have different actual lifetimes (the one defined first outlives the one defined second). If the lifetimes had to actually match, we'd always have to define all the variables on the same line!
Some more detailed information is available in RFC #738 — Variance.
Upvotes: 7