Why does a program compile despite an apparent lifetime mismatch?
Asked Answered
R

1

11

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?

Racehorse answered 30/6, 2020 at 14:1 Comment(2)
There's no way you can implement get and set to make convert unsound. Try it.Connivent
@trentcl not without using unsafe. But in the case I'm writing, I am using unsafe, but "safely", and it is the case that convert is unsound (but I appreciate that's just reflecting that my unsafe isn't quite safe enough).Racehorse
D
11

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.

Derbyshire answered 30/6, 2020 at 15:36 Comment(2)
Thanks! And to stop the variance, I defined Value as Cell<&'v ()> which then causes a compile-time mismatch.Racehorse
I meant contra, so I'll sneakily remove my comment and integrate it in the answer... with the fix.Derbyshire

© 2022 - 2024 — McMap. All rights reserved.