A simple case of lifetime of reference to reference

In Rust Cheatsheet, there is the following correct code (in the advanced part, in references to references):

#![allow(unused)]

fn main() {
    let mut ra= &mut S;
    let rval = f1lr(&&*ra);

    println!("{:?}, {:?}", *ra, *rval);  // output: S, S
}

#[derive(Default, Debug)]
struct S;

fn f1lr<'b, 'a>(rb: &'b &'a S) -> &'a S { *rb }

I don't understand why is this valid. I think ra and rval both point to the same S and ra is an exclusive reference. I think this is the gist of the reference to reference part of the cheat sheet. But I just can't grasp it.

This is allowed because rval is derived from ra within the same function as the use. In short, the compiler interprets your code like this:

#![allow(unused)]

fn main() {
    let mut ra= &mut S;
    let ra_shared = &*ra;
    let rval = f1lr(&ra_shared);

    println!("{:?}, {:?}", *ra_shared, *rval);  // output: S, S
}

This is the same sort relaxing of the rules that let you move one field out of a struct leaving it partially initialized— As long as everything is in order when you interface with outside code, the compiler lets you take certain shortcuts locally to avoid excess verbosity.

So even if we have an exclusive reference, we can still dereference it and acquire a shared reference with no consequence, right?

Maybe it's so because I tried a valid code like this. Quite a simple yet new concept for me.

#![allow(unused)]

fn main() {
    let mut ra= &mut S;
    let rval = f1lr(&&*ra);
    let rval2 = &*ra;

    println!("{:?}, {:?}, {:?}", ra, rval, rval2);  // output: S, S, S
}

#[derive(Debug)]
struct S;

fn f1lr<'b, 'a>(rb: &'b &'a S) -> &'a S { *rb }

Does this relaxing have any limitations or prerequisites? Or where is the doc for this relaxing? I mean, what does "in order" mean?

It does, but the details are subject to change in future compiler versions¹. I think the particular behavior you’re seeing here is described in the non-lexical lifetimes RFC.

Âą Backwards compatibility is guaranteed, so if something is valid now, it will continue to be valid in future compilers. They might start accepting programs that are currently rejected, though.

1 Like

That's of much help. Thanks a lot!