Why the code compiles with different lifetime?

Hello,

The code below tries to determine whether two reference contains exactly same life time.
fx accept some unnamed life time and early bound it to another function, which in return only accept argument of same lifetime.

The argument is invariant. But the f(&mut &mut c); compiles, which surprises me.
Anyone could explains this?

#[cfg(all())]
fn main() {
    fn fx<'a, T: 'a>(r: &mut &'a mut T) -> fn(&mut &'a mut T) {
        fn x<'b, T: 'b>(_: &mut &'b mut T) {}
        let f = x::<'a, T>;
        f(r);
        f
    }

    let mut a = 1;
    {
        let mut b = &mut a;
        let f = fx(&mut b);

        {
            let mut c = &mut b;
            f(&mut &mut c);
        };

        println!("{}", b);
    }
}

well, I guess you missed the auto deref feature.

There's at least three things going on that are likely to be confusing you.


First: You might be confused as to what you're testing.

            let mut c = &mut b;
            f(&mut &mut c);

You're passing in a &mut &mut &mut &mut i32. Maybe you meant this.

            f(c);

Second: shorthand fn syntax takes precedence over lifetime elision. So this:

fn fx<'a, T: 'a>(r: &mut &'a mut T) -> fn(&mut &'a mut T) {

Means this:

fn fx<'a, T: 'a>(r: &mut &'a mut T) -> for<'any> fn(&'any mut &'a mut T) {

And not this:

fn fx<'r, 'a, T: 'a>(r: &'r mut &'a mut T) -> fn(&'r mut &'a mut T) {

Third: Function pointers input argument types are contravariant. That means, for example, if I have a fn(&'a str), I can coerce it to a fn(&'static str).

And it also means that a fn(&'r mut &'a mut T) can be coerced into a fn(&'c mut &'a mut T), so long as 'c: 'r. (Remember, &'x mut U is covariant in 'x, not invariant.)

So even this version still compiles.


If we force 'r in the function pointer to be invariant, we finally get an error.

2 Likes

I think the point was not the outer, but the inner lifetime, i.e. 'a in &'_ mut &'a mut T. They should be different (since one referenced value is used after the other one already gone out of scope), and they're invariant, since they are behind &mut.

I don't understand what you mean by

They should be different (since one referenced value is used after the other one already gone out of scope).

What uses and values going out of scoped do you mean?


The inner lifetime is the same at both call sites because the argument at both callsites is &mut b, where b is &'a mut a. And b is in scope and the lifetime 'a valid through the println!.

Ah. Sorry, I misread the code - thought somehow that the root values (i.e. the Ts) were different. (If there were indeed different, we'd got the expected "value doesn't live long enough" - playground)

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.