Mutable references and aliasing
In Rust a mutable reference, &mut T, is the one way to change a value without taking ownership of it. The rule is the mirror image of the shared borrow: one mutable reference at a time, and no shared reference while it is alive. That single rule removes data races and iterator invalidation before the program runs.
The lesson shows the working case first, then the three errors the rule produces. They are the errors a new Rust programmer meets most often, and each one names the two borrows that conflict, so the fix is usually a reordering rather than a redesign.
One writer
A parameter of type &mut i32 lets a function write to the caller value. The caller must have a mutable binding and must pass &mut count; the function dereferences with *value to reach the number.
The program prints six, because the function added one to five. Nothing was returned and nothing was copied: the write went through the reference into the caller binding.
fn add_one(value: &mut i32) {
*value += 1;
}
fn main() {
let mut count = 5;
add_one(&mut count);
println!("{count}");
}
rustc --edition 2024 increment.rs
./increment
6
A mutable reference is not a pointer that can be copied around freely: it may not be duplicated, and while it exists the owner is frozen. That is what makes the write safe without a lock or a runtime check.
Two mutable borrows
Taking a second &mut while the first is still alive is E0499. The message shows both borrows and the line that uses the first one later, which is why the compiler considers it alive. Without that later use the first borrow would have ended.
The program below creates two and prints both, so both are alive at the print. The compiler rejects it at the second borrow, before the print is even considered.
fn main() {
let mut text = String::from("hello");
let a = &mut text;
let b = &mut text;
println!("{a} {b}");
}
rustc --edition 2024 two_mut_err.rs
error[E0499]: cannot borrow `text` as mutable more than once at a time
--> two_mut_err.rs:4:13
|
3 | let a = &mut text;
| --------- first mutable borrow occurs here
4 | let b = &mut text;
| ^^^^^^^^^ second mutable borrow occurs here
5 | println!("{a} {b}");
| - first borrow later used here
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0499`.
The fix is to use the first borrow before the second begins, or to merge the two writes into one function that takes a single reference. The lesson on methods shows the same rule for &mut self.
A reader and a writer
A shared borrow and a mutable borrow cannot coexist, and the error code is E0502. The reason is the same: the reader could see the value change underneath it, which is exactly the class of bug the rule prevents.
The program takes a shared reference first, then a mutable one, and prints both. The message names the immutable borrow, the mutable borrow and the later use of the reader.
fn main() {
let mut text = String::from("hello");
let shared = &text;
let mutable = &mut text;
println!("{shared} {mutable}");
}
rustc --edition 2024 shared_mut_err.rs
error[E0502]: cannot borrow `text` as mutable because it is also borrowed as immutable
--> shared_mut_err.rs:4:19
|
3 | let shared = &text;
| ----- immutable borrow occurs here
4 | let mutable = &mut text;
| ^^^^^^^^^ mutable borrow occurs here
5 | println!("{shared} {mutable}");
| ------ immutable borrow later used here
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0502`.
Reading before writing is fine as long as the read finishes first. The next section shows exactly that program with the print moved, and it compiles, because the borrow ends where it is last used.
A borrow ends at its last use
A borrow does not last until the end of the scope. The compiler ends it at the last line that uses it, which is why the program below can print through a shared reference and then take a mutable one. This is often called non-lexical lifetimes, and it has been the rule since edition 2018.
The output has two lines: the old value through the shared borrow, then the new value through the mutable one. Both borrows are in the same scope and never overlap.
fn main() {
let mut text = String::from("hello");
let shared = &text;
println!("{shared}");
let mutable = &mut text;
*mutable = String::from("bye");
println!("{mutable}");
}
rustc --edition 2024 nll.rs
./nll
hello
bye
The rule also applies to returning a reference from a block: the borrow ends where the reference is last used, not at the closing brace. The lesson on lifetimes makes the same point from the other side.
A binding that is not mutable
&mut needs a mutable binding to borrow from. Taking one from a plain let is E0596, and the help line adds the missing keyword in the exact place. The code is different from E0499 because the problem is the binding, not another borrow.
The program below asks for a mutable reference to an immutable string. The compiler reports the &mut text expression and suggests let mut text.
fn main() {
let text = String::from("hello");
let mutable = &mut text;
println!("{mutable}");
}
rustc --edition 2024 immutable_err.rs
error[E0596]: cannot borrow `text` as mutable, as it is not declared as mutable
--> immutable_err.rs:3:19
|
3 | let mutable = &mut text;
| ^^^^^^^^^ cannot borrow as mutable
|
help: consider changing this to be mutable
|
2 | let mut text = String::from("hello");
| +++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0596`.
Making the binding mutable is the whole fix. The lesson on const and mutability explains when the keyword belongs on the binding and when the value itself has to change shape.
Exercises
- Write a function that takes two mutable references and swaps the values. Why does it need two separate borrows of two variables rather than two of the same one?
- In the two-mutable program, move the print of the first reference before the second borrow. Does the program compile now?
- Change the last program to take the reference from a temporary and explain why the error changes.
Questions
A new set of questions in a new order every time. Choose one answer for each, then check.
-
How many mutable references to one value may exist at a time?
Answer
One
A single writer is what makes the write safe without a lock, and the compiler enforces it.
-
May a shared reference and a mutable reference be alive at the same time?
Answer
No, the reader could see the value change
The two borrows would alias, so the compiler ends one before the other may start.
-
When does a borrow end?
Answer
At the last line that uses it
Non-lexical lifetimes end a borrow at its last use, which allows a mutable borrow afterwards.
-
What does a mutable reference require of the binding it borrows?
Answer
That the binding is declared with mut
Borrowing mutably requires a mutable owner, otherwise the compiler reports E0596.
-
What does this program print?
fn add_one(value: &mut i32) { *value += 1; } fn main() { let mut count = 5; add_one(&mut count); println!("{count}"); }Answer
6
The function writes through the reference, and the caller prints the changed value.
-
What does this program print?
fn main() { let mut text = String::from("hello"); let shared = &text; println!("{shared}"); let mutable = &mut text; *mutable = String::from("bye"); println!("{mutable}"); }Answer
hello / bye
The shared borrow ends at its print, so the mutable borrow may start afterwards.
-
What is the first line of the compiler output for this program?
fn main() { let mut text = String::from("hello"); let a = &mut text; let b = &mut text; println!("{a} {b}"); }Answer
error[E0499]: cannot borrow
textas mutable more than once at a timeTwo writable borrows of one value are the aliasing the rule forbids, and E0499 names it.
-
What is the first line of the compiler output for this program?
fn main() { let mut text = String::from("hello"); let shared = &text; let mutable = &mut text; println!("{shared} {mutable}"); }Answer
error[E0502]: cannot borrow
textas mutable because it is also borrowed as immutableThe shared borrow is still alive at the mutable one, and the message names the reader and the writer.
-
What is the first line of the compiler output for this program?
fn main() { let text = String::from("hello"); let mutable = &mut text; println!("{mutable}"); }Answer
error[E0596]: cannot borrow
textas mutable, as it is not declared as mutableThe binding has no mut, and the help line adds it to the let statement.
-
What does this program print?
fn main() { let mut total = 10; { let reference = &mut total; *reference += 5; } println!("{total}"); }Answer
15
The block writes five through the reference, and the borrow ends at the closing brace.
The code on this page was built with rustc 1.96.1 and run on Alpine Linux 3.24.3 (x86_64) in a Docker container.
Article author: Arthur Isaev