Two mutable references

Why is this happening?

cargo 1.51.0 (43b129a20 2021-03-16)

fn main() {
    let mut s = String::from("hello");
    let r1 = &mut s;
    let r2 = &mut s; // defined and not used

    change(&mut s);

//    println!("{}, {}", r1, r2);
    println!("{}", s);
}

fn change(some_string: &mut String) {
    some_string.push_str(", world!");
}

The above code will compile, but there are warnings。

$ cargo build
warning: unused variable: `r1`
  --> src/main.rs:17:9
   |
17 |     let r1 = &mut s;
   |         ^^ help: if this is intentional, prefix it with an underscore: `_r1`
   |
   = note: `#[warn(unused_variables)]` on by default

warning: unused variable: `r2`
  --> src/main.rs:18:9
   |
18 |     let r2 = &mut s;
   |         ^^ help: if this is intentional, prefix it with an underscore: `_r2`

warning: 2 warnings emitted

    Finished dev [unoptimized + debuginfo] target(s) in 0.00s

The warnings are about variables that you defined but never used. You just used s directly instead.

If you uncomment the first println!, there's a borrow checker error. Rust's borrow checking rules don't allow you to interleave non-exclusive borrows like r1 and r2 with mutations. So:

   let r1 = &mut s;
   let r2 = &mut s; // You can now no longer use r1
   change(&mut s);  // You can now no longer use r2, either

    // So this is an error:
    // println!("{}, {}", r1, r2);

I think even r2 is not used, compiler should output error message.

It used to, but now the compiler supports non-lexical lifetimes (NLL).

When the compiler looks for multiple mutable references, it looks at its last use, not when it goes out of scope to determine if they overlap.