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