Skip to content
Select theme

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.

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 here
4 | scores.push(40);
| ^^^^^^^^^^^^^^^ mutable borrow occurs here
5 | 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.

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.

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:

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.

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:

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:

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 here
5 | | }
| |_____- 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:

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.)

Two neighbours that look similar but carry different codes:

  • Two &mut borrows 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.