Understanding References with 3 simple programs

I have a rust program 1 as follows

fn main() {
    let mut s = String::from("Mutable");
    let r1 = &mut s;
    println!("s : {} ",s);
    println!("r1 : {} ",r1);
}

This gave me a compile error
From what I understand, the value of s is now referred by r1, hence we cannot use both together.

So the following Program 2 works

fn main() {
    let mut s = String::from("Mutable");
    let r1 = &mut s;
    println!("r1 : {} ",r1);
}

Program 2 made sense because the value is now referred by r1.

But the following program 3 also works

fn main() {
    let mut s = String::from("Mutable");
    let r1 = &mut s;
    println!("s : {} ",s);
}

Why does program 3 work? I thought r1 now holds reference to the value and not s. Shouldn't compilation fail just like Program 1 ?

r1 holds a reference to s which gives it temporary access to s's value. Once r1 is no longer used s becomes accessible again. In your example r1 is never used so s immediately becomes accessible again. If instead you tried to use it after s's print you'll see it fail. For example this gives you a compile error:

fn main() {
    let mut s = String::from("Mutable");
    let r1 = &mut s;
    println!("s : {} ",s);
    println!("r1 : {}",r1);
}
2 Likes

Interesting. So I tried to reverse the statements. And that compiled successfully.

fn main() {
    let mut s = String::from("Mutable");
    let r1 = &mut s;
    println!("r1 : {}",r1);
    println!("s : {} ",s);
}

So the word "temporary" is key here. So long as r1 is used it will have access to the value, unless and until s gets used somewhere in the program.

In older versions of Rust, you would get a compile error unless you introduced an { let r1 = ... } block to limit the scope of r1. This proved to be not very ergonomic, so "non-lexical lifetimes" (NLL) were added to the language to fix that, resulting in what you've observed.

3 Likes