Are lifetimes always same for immutable references of same variable

For example, does r2 have same lifetime as r1?

let a: u8 = 42;
let r1 = &a;
{
	let r2 = &a;
}

According to variance, we can implement InvariantLifetime as

pub struct Invariant<T>(PhantomData<*mut T>);
pub struct InvariantLifetime<'id>(Invariant<&'id ()>);

Then I have:

fn get_lifetime<'a, T>(_: &'a T) -> InvariantLifetime<'a> {InvariantLifetime::new()}
let a: u8 = 42;
let r1 = &a;
let mut r1t: InvariantLifetime<>;
r1t = get_lifetime(r1);
{
	let r2 =&a;
	let mut r2t = get_lifetime(r2);
	r2t = r1t;
	r1t = r2t;
}

The above code can be compiled which imply two references have same lifetime.
But they are in different scope!

Immutable references are copy. When copy is made the "new"/independent lifetime (of t1t) is a bound on the source borrow(a) rather than the other bound variable(r1).

    let a: u8 = 42;
    let mut r1t: InvariantLifetime<>;
    {
        let r1 = &a;
        r1t = get_lifetime(r1);
    }

No, lifetimes are not always the same for references to the same variable. There can be at least as many different lifetimes for references to the same variable as there are independent borrow expressions of that variable.

That said,

It can, but it doesn't have to.

Lifetimes are not deduced; they are inferred. That means for any given piece of code, there may be several possible lifetimes that make it work. For each borrow expression, the compiler can pick any valid lifetime as long as it meets all the criteria.

Consider this example with a single lifetime:

let x = "Cryptid";
let r1;
{
    r1 = &x;
}
println!("{}", r1);

The lifetime of the borrow expression &x is not constrained to the inner scope it is created in. The compiler is free to choose "the entire scope of the variable x" as the lifetime of &x. The above code compiles and does what you would expect. It's the same with your example:

let a: u8 = 42;
let r1 = &a;
let mut r1t: InvariantLifetime<>;
r1t = get_lifetime(r1);
{
	let r2 =&a;
	let mut r2t = get_lifetime(r2);
	r2t = r1t;
	r1t = r2t;
}

The compiler is still free to choose "the entire scope of a" as the lifetime of both borrow expressions, and since that makes the code compile, that's what it will do.

Incidentally, although it doesn't matter for this example, I don't think get_lifetime does what you want it to (make an "invariant copy" of the input lifetime). Instead, it materializes a new invariant lifetime derived from the input lifetime. Here's the example I would have written that uses &mut &'a T as the parameter to make sure the compiler can't use the variance of &'a T to decouple the lifetimes.

This question is a good opportunity to mention a common misconception: that the lifetime of a property of the referenced value. It's not; the reference has a lifetime, and that lifetime is checked to be compatible with the referenced value.

This is most obvious with &mut -- two mutable references to the same thing better not have the same lifetimes, as then they'd overlap, which of course isn't allowed.

4 Likes

Thank you for the kind reply.
So when we write a reference, there is no insurance about what the lifetime really are?

Does it in some way conflict with the discussion in Lifetime(a) = lifetime(b) iff (a=b)?. If the discussion in that post is true, then we might be able to discriminate different variable with their lifetime, which is just what I'm trying to do.

In order to exploit the problem further, in that post, I construct a more strange situation where references to different variables share same lifetime.

If the compiler is free to choose a lifetime that make a given code works, then can we deliberately fix a lifetime so as to discriminate two variable? Unfortunately &mut &'a T approach doesn't work, the code could still be compiled. Is there any way to do that?

Thanks for your reply.

Do you mean" lifetiem is a property of the referenced value" is a common misconception?
So let me make it clear. Every values have a lifetime. Every references also have a lifetime. The lifetime of the value value should be a subtype(live longer than) of the lifetime of the reference to it &value. When we type &'a value, the 'a is the lifetime of reference &value rather than value ?

If that's right, then I'm totally agree with above statement. That's why I'm shocked to see two separate references defined in two different scope have same lifetimes.

Thanks for your reply.

Do you mean Copy Trait?
If so, yes. &'a T is a Copy.
Maybe after the copy the lifetime of reference could shrink(certainly it should not expand). After copy &'a T become &'b T and we have bound 'a: 'b.
In order to expel that effect, we can exploit original code:

let a: u8 = 42;
let r1 = &a;
let mut r1t: InvariantLifetime<>;
r1t = get_lifetime(r1);
{
	let r2 =&a;
	let mut r2t = get_lifetime(r2);
	r2t = r1t;
	r1t = r2t;
}
{
	let r2 =&a;
	let mut r2t = get_lifetime(r2);
	r2t = r1t;
	r1t = r2t;
}

Even after the copy, r1t should must cover two block. While two r2 sohuld have two different lifetime in different scope.
However, this code could still be compiled.

I also try to implement an function like get_lifetime in your case. But I can't get why this function works. The extern link to variance is invalid now. Could you please provide a new link to the variance documentation? Thanks a lot.