Env: rustc 1.82.0 (f6e511eec 2024-10-15)
Question: Dereferencing vs. Direct Borrowing in Rust
I’m confused about the difference between *&T
and T
in borrowing. The following code compiles with let b = &mut *a
, but fails with let b = &mut x
. Why does *a
behave differently from x
in this context?
Additionally, uncommenting the last line (*b = ...
) causes the error:
cannot assign to
*a
because it is borrowed
fn annoying_borrow() {
let mut x = Box::new(1);
let a = &mut x;
let b = &mut *a;
// let b = &mut x; // error: cannot borrow `x` as mutable more than once at a time
*a = Box::new(2);
// *b = Box::new(3); // error: `*a` is assigned to here but it was already borrowed
}
Does using the *a
instead of the x
here further imply the subtle difference between them? Emmm, what I really want to explore is not the difference between *a
and x
, but the rules of borrowing in the Rust type system (compiler).
Background:
I’ve referenced Programming Rust, 2nd Ed. (Chapter 5), but I cannot fully comprehend this knowledge:
Shared access is read-only access.
Values borrowed by shared references are read-only. Across the lifetime of a shared reference, neither its referent, nor anything reachable from that referent, can be changed by anything. There exist no live mutable references to anything in that structure, its owner is held read-only, and so on. It’s really frozen.
Mutable access is exclusive access.
A value borrowed by a mutable reference is reachable exclusively via that reference. Across the lifetime of a mutable reference, there is no other usable path to its referent or to any value reachable from there. The only references whose lifetimes may overlap with a mutable reference are those you borrow from the mutable reference itself.
I wonder if there is any unified and detailed explanation of borrowing in Rust. Perhaps there used to be a clear set of rules about borrowing in Rust, but with the compiler continuously relaxing syntax restrictions (to facilitate coding in certain scenarios?), the current rules are more chaotic?
Mutable references can be 'inactive'. Only one mutable reference can be actively holding exclusive mutable access, but just because a reference exists, does not mean it holds access. When you borrow through a mutable reference, that access is transferred to the borrowing reference.
This means that you can create a mutable reference b through the mutable reference a that has access, transferring the access from a to b, until b is dropped when it is reverted to a. You can not use a directly whilst b exists, because it does not have access, like how x does not have access whilst a exists.
Additionally you can create a shared reference from a mutable reference, in which case, the mutable references access is downgraded to shared until the last reference borrowed from it is dropped.