To give some practical examples beyond @2e71828's explanation: Variance can be different for each lifetime. E.g. fn(&'a u8) -> &'b u8
is covariant in 'b
and contravariant in 'a
. And variance applies to lifetime variables as well as type variables. E.g. &'a T
is covariant in 'a
and covariant in T
, whereas &'a mut T
is covariant in 'a
but invariant in T
.
As I explored further in the post I’ve already linked above, variance of type variables ultimately affects variance of lifetimes. The type Box<&'a Foo>
is covariant in 'a
; which can be determined by looking at the variance of Box<T>
and &'b S
(here T
, S
, and 'b
are type/lifetime variables).
Box<&'a Foo>
is composed by: plugging in 'a
for 'b
and Foo
for S
in &'b S
, and then pluggin the resulting &'a Foo
for T
in Box<T>
. So the lifetime 'b
is appearing in the first/only (“T
”) type argument of Box<T>
, and then inside of that in the first/only (“'b
”) lifetime argument of &'b S
. The variance of these arguments, i.e. fact that Box<T>
is covariant in T
and &'b S
is covariant in 'b
, combine to let us derive that Box<&'a Foo>
is covariant in 'a
.
You could try to apply similar reasoning to infer e.g. that &'a mut &'b mut u8
is covariant in 'a
and invariant in 'b
.
The reason why this reasoning/deduction works becomes clear if you expand the definition of variance in each case.
“Box<T>
is covariant in T
” means that for every subtype U
of V
, Box<U>
is a subtype of Box<V>
and “&'b S
is covariant in 'b
” means that for every lifetime 'x
that outlives 'y
, and every type S
, &'x S
is a subtype of &'y S
.
Now, “Box<&'a Foo>
is covariant in 'a
” means that for every lifetime 'l
that outlives 'm
, Box<&'l Foo>
is a subtype of Box<&'m Foo>
. We can prove that this is the case based on the previous two statements, by instantiating type variables and lifetime variables:
U
:= &'l Foo
V
:= &'m Foo
'x
:= 'l
'y
:= 'm
S
:= Foo
Note: What do I mean by “instantiating”? Replacing the variables with these above instantiations, and we can infer
- from “
Box<T>
is covariant in T
”, that
- if
U
is a subtype of V
, then Box<U>
is a subtype of Box<V>
, thus in particular:
- if
&'l Foo
is a subtype of &'m Foo
, Box<&'l Foo>
is a subtype of Box<&'m Foo>
,
- from “
&'b S
is covariant in 'b
”, that
- if
'x
outlives 'y
, then &'x S
is a subtype of &'y S
, thus in particular:
- if
'l
outlives 'm
, then &'l Foo
is a subtype of &'m Foo
.
Putting things together, “Box<T>
is covariant in T
” gives us the desired relation Box<&'l Foo>
is a subtype of Box<&'m Foo>
, provided that &'l Foo
is a subtype of &'m Foo
. And &'l Foo
is a subtype of &'m Foo
from the fact that “&'b S
is covariant in 'b
”, provided that 'l
outlives 'm
.
This chain of implications shows indeed that for every lifetime 'l
that outlives 'm
, Box<&'l Foo>
is a subtype of Box<&'m Foo>
.