You got it all mixed up. You need to swap the names long
and short
in your source code to correctly reflect what’s going on. (Ah, now that I’ve finished writing this, @Yandros already noticed this, too.)
A reference type &'a T
is only a valid type if T: 'a
, i.e. the lifetime 'a
of the reference must be such that T
outlives 'a
(you read the :
as “outlives”). This notation, T: 'a
means, roughly, that (the lifetime of) all references (transitively) contained inside of T
must outlive 'a
. Finally, 'b: 'a
between two references, read as 'b'
outlives 'a
means that 'b
is “longer” than 'a
. It does not need to be strictly longer (i.e. 'a: 'a
is true), and longer vs. shorter means, thinking about lifetimes roughly as scopes / code-blocks, that the “shorter” lifetime is entirely contained within the longer one. The intuition for why “&'a T
is only a valid type if T: 'a
” is something like this: A reference of type &'a T
is supposed to be valid for its whole lifetime 'a
, and a valid reference must point to valid data. If that data itself contains references, then the data is only valid while all the contained references are valid. Thus, throughout all of 'a
all of the references inside T
must still be valid.
In your example you have a nested reference, so we need to apply the knowledge from the previous paragraph to the case where T
is of the form &'b S
. Now all references in T
is the reference &'b S
itself as well as all references inside S
. Thus T: 'a
, i.e. &'b S: 'a
is fulfilled whenever both 'b: 'a
and S: 'a
.
We can conclude that the type &'a &'b S
is valid whenever &'b S: 'a
holds and &'b S
is valid, i.e. whenever 'b: 'a
and S: 'a
as well as S: 'b
. The “outlives” relation is transitive, hence S: 'a
follows from S: 'b
and 'b: 'a
. The final conclusion is that &'a &'b S
is valid whenever 'b: 'a
and S: 'b
.
For the concrete type &'a &'b str
, the type str
does not contain any more references, hence str: 'b
is trivially true for all lifetimes 'b
. (The fact “str: 'b
is true for all lifetimes 'b
” is usually written as “str: 'static
”. The lifetime 'static
is longer than [i.e. outlives] every other livetime, this means 'static: 'b
is true for any 'b
. Then str: 'b
follows transitively from str: 'static
and 'static: 'b
.) This means that &'a &'b str
is valid whenever 'b: 'a
, i.e. 'b
is longer than 'a
. By the way, the reason that you can still write
struct S<'short, 'long> {
data: &'short &'long str,
}
as opposed to
struct S<'short, 'long: 'short> {
data: &'short &'long str,
}
is that the rust compiler infers and enforces these relations between lifetimes implicitly and automatically. Both versions of the code do the same thing, one is just more explicit.