How to fix error E0502: cannot borrow as mutable while it is borrowed as immutable
Short answer: a shared borrow (&) of a value was still in use when the code tried to take a
&mut of the same value. Rust allows one mutable borrow or any number of shared borrows — never
both at once. Finish the read before the write starts, or copy the value you need so no borrow is
held across the mutation.
The error
Section titled “The error”fn main() { let mut scores = vec![10, 20, 30]; let first = &scores[0]; scores.push(40); println!("first is {first}");}Compiling that with cargo build prints:
error[E0502]: cannot borrow `scores` as mutable because it is also borrowed as immutable --> src/main.rs:4:5 |3 | let first = &scores[0]; | ------ immutable borrow occurs here4 | scores.push(40); | ^^^^^^^^^^^^^^^ mutable borrow occurs here5 | println!("first is {first}"); | ----- immutable borrow later used here
For more information about this error, try `rustc --explain E0502`.first borrows scores immutably, and because first is still used after scores.push(40), the
compiler treats that borrow as alive when the mutable borrow starts. Rust rejects the combination.
Why Rust stops this
Section titled “Why Rust stops this”push may have to grow the vector. When the vector outgrows its allocation, it allocates a bigger
buffer elsewhere, copies the elements over, and frees the old buffer. first points into that old
buffer — after the push, it would be a reference to freed memory. Rust’s rule against mixing
live shared and mutable borrows is what turns that whole bug class into a compile error instead of
a crash or garbage values.
The message says also borrowed as immutable because the immutable borrow is the one already there: the read outlived its welcome, and the mutable borrow just walked in on it.
“Borrowed” means “still in use”
Section titled ““Borrowed” means “still in use””Like E0499, this error is about borrows that are alive at the same time, not about how many
& expressions the file contains. A borrow ends at its last use, so finishing the read before
the write is enough — no extra scope needed:
fn main() { let mut scores = vec![10, 20, 30]; let first = &scores[0]; println!("first is {first}"); scores.push(40); println!("{scores:?}");}This prints:
first is 10[10, 20, 30, 40]first’s borrow ends at the println! line. By the time push runs, there is no shared borrow
left to conflict with, so the same two operations in a different order compile fine.
The fixes
Section titled “The fixes”Finish the read first — shown above. Reorder so every use of the reference happens before the mutation.
Copy the value out. If the read only needs the value, not a reference into the vector, take an
owned copy. i32 is Copy, so indexing already copies:
fn main() { let mut scores = vec![10, 20, 30]; let first = scores[0]; scores.push(40); println!("first is {first}, scores is {scores:?}");}This prints first is 10, scores is [10, 20, 30, 40]. For types that are not Copy, write
let first = scores[0].clone(); — the clone holds no borrow, so it can outlive the mutation.
The shape to recognise: mutating inside a loop
Section titled “The shape to recognise: mutating inside a loop”The most common real-world version is pushing to a collection while iterating over it:
fn main() { let mut names = vec![String::from("Ada")]; for name in &names { names.push(name.to_uppercase()); } println!("{names:?}");}The for loop holds one shared borrow of names for the whole loop body, so names.push inside
the body collides with it:
error[E0502]: cannot borrow `names` as mutable because it is also borrowed as immutable --> src/main.rs:4:9 |3 | for name in &names { | - ------ | | | | | immutable borrow occurs here | _____| immutable borrow later used here | |4 | | names.push(name.to_uppercase()); | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here5 | | } | |_____- this for loop borrows `names` immutably, preventing mutation within its body | = help: consider using an index-based loop instead, or collecting modifications into a separate collection
For more information about this error, try `rustc --explain E0502`.The = help: line states the fix: collect the new items first, then add them once the loop — and
its borrow — is over:
fn main() { let mut names = vec![String::from("Ada")]; let uppercased: Vec<String> = names.iter().map(|n| n.to_uppercase()).collect(); names.extend(uppercased); println!("{names:?}");}This prints ["Ada", "ADA"]. The collect runs while the shared borrow is alive, extend runs
after the borrow has ended. (The loop version would also have never stopped — every push grows
what the loop is iterating — so the compiler is saving you from that too.)
Not this error
Section titled “Not this error”Two neighbours that look similar but carry different codes:
- Two
&mutborrows of the same value alive at once is E0499 (cannot borrow as mutable more than once). There both handles want to write; here one only reads. - Holding a borrow across a move of the value is E0505 (
cannot move out of ... because it is borrowed) — same overlap idea, but the problem is moving, not mutating.
Related errors
Section titled “Related errors”- E0499 — cannot borrow as mutable more than once
- E0382 — borrow of moved value
E0505— cannot move out of a value because it is borrowed