This borrow using &* works, but why?

let mut a = 1;
let b = &mut a;
let c = &mut *b; // changed to `&mut` for simplicity

leads to:

image

  • black arrows represent the relationship between an address and the memory it points to ("C pointers");

  • blue arrows represent the Stacked Borrows "authorization" / validity of such pointers:

    1. b is a valid pointer because it directly borrows the memory it points to (and it does so in an exclusive / unique manner, that's what the mut in &mut means);

    2. Then c is valid in and on itself thanks to it (also exclusively) reborrowing a through the borrow b; it is as if c had borrowed b's "license to access a": as long as c is valid, b cannot access a.

In other words, as soon as b accesses a, it means that c must have (maybe implicitly) forfeited its "license to access a", so as to have given it back to b. At that point, c becomes an invalidated / stale pointer: even though c may still live in memory, and even though its value is a memory address that points to b, Rust semantics forbid that such address be used to access a.

And this is what happens when you do:

let d = *b;

image

  • That's why, for instance, doing afterwards let e = *c; leads to a compilation error.

  • In your example however, c was not an exlusive (&mut) borrow but a shared (&) borrow; the situation is a bit more complex because b and c can still share access to a, (e.g., both can read it). That's why this playground does compile. It's just that while c is valid, b no longer has "exclusive access privileges" that would allow it to mutate a, so if b ever does mutate a, then the same reasoning as before applies and it means that c must have gotten invalidated beforehand.

    That's why the following code also fails to compile:

    let mut a = 1;
    let b = &mut a;
    let c = &*b;
    *b = 42; // use `&mut`-ness of `b`
    let e = *c;
    

Now, regarding:

let mut a = 1;
let b = &mut a;
let c = &a;

we have:

image

  • Indeed, c is directly borrowing from a even though b was exclusively borrowing a. This means that b must have implicitly forfeited its borrow and gotten invalidated.

This means that the following line let d = *b; is actually using an invalidated borrow, hence the compilation error.

  • But if it had been let b = &a;, then the borrow would have been shared and c would have been allowed to also get shared access to a without invalidating the b borrow (Playground).
14 Likes