Part 2 · The compile-time machine I — borrows & lifetimes
`&mut` does not mean mutable
The keyword is misnamed, and the misnaming costs people months. Read it as "exclusive" and a whole category of confusion disappears.
What this lesson is trying to break (3)
- `&T` means the data cannot change.
- `&mut T` is the mutable version of `&T`, and mutation is the point.
- Passing a `&mut` to a function moves it away permanently.
This lesson is short and it may be the highest ratio of value to length on the site. The problem it fixes is a naming mistake.
&T is an immutable reference: the data behind it cannot change. &mut T is
the mutable one. The distinction is about whether you may write, the same
way const works in TypeScript or final in Java.
&T does not guarantee the data will not change. It is trivial to disprove:
Cell::set, RefCell::borrow_mut, Mutex::lock and every atomic all mutate
through &self. If & meant immutable, none of those could exist.
The real axis is sharing, not mutation.
&T is a shared reference: others may hold one too.
&mut T is an exclusive reference: while it exists, nothing
else in the entire program can see this data.
Mutation permission is a consequence — it is safe to write precisely when
you are the only observer.
The Rust community knows about this. You will hear people say “unique reference” or “exclusive reference” in preference to “mutable reference”, and the reference documentation increasingly uses shared and mutable rather than immutable and mutable. Renaming the keyword was considered and rejected as too disruptive. So the name stayed wrong, and everyone learns around it.
Start reading it correctly now and you skip the confusion entirely.
The proof
use std::cell::Cell;
fn bump(counter: &Cell<u32>) {
counter.set(counter.get() + 1);
}
fn main() {
let c = Cell::new(0);
bump(&c);
bump(&c);
println!("{}", c.get());
}Note that c is not declared mut, and bump takes a shared reference.
It compiles and prints 2.
A value was mutated twice, through a shared reference, with no mut anywhere in
sight.
This is not a loophole — it is the design working as intended. Cell is built on
UnsafeCell, the single primitive in the language that permits mutation through
&. Everything with interior mutability — Cell, RefCell, Mutex,
RwLock, AtomicUsize, OnceLock — bottoms out there.
So the accurate statements are:
&Tpromises sharing is permitted. It does not promise immutability.&mut Tpromises nothing else can observe this while it lives.
The second promise is the strong one. It is what powers the optimisations from
2.1, and it is what makes &mut the
interesting half of the pair.
This is 2.6 in preview. Keep the question of how a type can be allowed to do this — the answer is that it moves the aliasing check from compile time to run time.
Why &mut T is not Copy
Once you read &mut as exclusive, a rule that looks arbitrary becomes forced.
&T is Copy. You can hand out as many as you like — sharing is exactly what it
permits.
&mut T is not Copy, and it cannot be. A copy would give you two exclusive
references to the same data, and “exclusive” would then mean nothing. The
restriction is not a safety tax bolted on; it is what the word means.
let mut x = 5;
let a = &mut x;
let b = a; // moves — a is now dead
// println!("{a}"); // E0382: use of moved value
*b += 1;
&mut behaves like an owned value because, for the duration of its region, it
effectively is the owner.
Reborrowing — why this doesn’t ruin everything
If &mut moves, how does this work?
fn bump(n: &mut i32) { *n += 1; }
fn main() {
let mut x = 0;
let r = &mut x;
bump(r);
bump(r); // r still usable?!
println!("{x}");
}
By the rule above, the first bump(r) should have moved r away. It compiles
and prints 2.
The mechanism is reborrowing. When you pass a &mut where a &mut is
expected, the compiler silently inserts &mut *r — a new, shorter-lived
exclusive reference derived from r. While the reborrow is alive, r is frozen
and unusable. When it ends (at the end of the call), r becomes usable again.
This is one of the most important invisible things the compiler does, and almost
nothing explains it, which is why “I thought &mut moves — so why does this
work?” is such a common stumble.
&mut *r · &mut **r
An explicit reborrow. The author is deliberately creating a shorter-lived exclusive reference, usually because inference did not pick a short enough region on its own, or to force a borrow to end before the next statement.
Seeing it written by hand is a small signal that someone hit a lifetime problem here and solved it deliberately.
Two-phase borrows
One more piece of compiler machinery, because it explains an apparent contradiction you will hit within a week.
let mut v = vec![1, 2, 3];
v.push(v.len()); // compiles
push needs &mut v. v.len() needs &v. Both appear to be live at the same
moment, which the rule from 2.1 forbids. Yet it compiles.
The reason is two-phase borrows. A &mut taken for an autoref method call
starts in a “reserved” phase, during which shared reads are still permitted. It
becomes truly exclusive only when the call actually begins — after the arguments
have been evaluated.
You do not need this often, but you need it once: the first time you write this and someone tells you the rule is absolute, you will want to know why the compiler disagrees.
Rewrite these two sentences without the words “mutable” or “immutable”:
- “
&mut selfmeans the method can modify the struct.” - “You can’t have a mutable and an immutable borrow at the same time.”
Then say which of the two rewrites changed the meaning rather than just the wording. One of them did.
What you should now be able to say
- The axis is shared versus exclusive, not immutable versus mutable.
&Tpermits sharing; it does not promise the data will not change —Cell::settakes&self.&mut Tpromises nothing else can observe this, which is what enables both safe mutation andnoalias.&mut Tcannot beCopy, because two exclusive references is a contradiction.- Reborrowing is why passing a
&mutdoes not consume it. - Two-phase borrows are why
v.push(v.len())compiles.
Next: 2.3 — Lifetimes are regions of code. That one is the hardest lesson on the site, and the one that pays best.