Given the following Rust program:
struct Value<'v>(&'v ());
struct Container {}
impl Container {
fn get<'v>(&'v self) -> Value<'v> {
todo!()
}
fn set<'v>(&'v self, x: Value<'v>) {
todo!()
}
}
fn convert<'v1, 'v2>(x: &'v1 Container, env: &'v2 Container) {
let root: Value<'v2> = env.get();
x.set(root);
}
I would expect convert
to be a compile time error as Value<'v2>
gets passed to x.set()
which requires a value of type Value<'v1>
- but it successfully compiles. There is no subtyping relationship between 'v1
and 'v2
. How has Rust inferred satisfying lifetimes?
The compiler is always allowed to re-borrow with a shorter lifetime.
In this case, what happens in:
fn convert<'v1, 'v2>(x: &'v1 Container, env: &'v2 Container) {
let root: Value<'v2> = env.get();
x.set(root);
}
Is that the compiler reborrows x
(aka (&*x)
) with a lifetime 'v3
, shorter than 'v2
, which is allowed due to the (inferred) variance of Value<'v>
(which matches &'v T
).
It is possible to change the inferred variance of Value<'v>
by changing the inner value:
&'v ()
(the current) is covariant.Cell<&'v ()>
is invariant.fn (&'v ()) -> ()
is contravariant, the inverse of covariant.Using an invariant Value<'v>
prevents unifying the lifetime with a fresh one, while using a contravariant Value<'v>
only allows unifying the lifetime with a greater one.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With