Rust Error E0502 Cannot Borrow as Mutable and Immutable
E0502 appears when you hold a reference into a value and then change that value. See the typical Vec case, why push is the danger, and four ways to resolve it.

error[E0502]: cannot borrow ... as mutable because it is also borrowed as immutable is the compiler stopping you from changing a value while something else is reading it. It is the error behind a vector that reallocates under a reference, a map that rehashes under an iterator, and the whole family of bugs known elsewhere as iterator invalidation. In C++ those are crashes at run time. In Rust they are this message at compile time, and the fix is almost always to shorten how long the shared borrow lives. This post reproduces the two forms you will actually write, a push while a reference exists and a mutation inside a loop, with the compiler's real output from Rust 1.99, and shows three fixes.
Why the rule exists
A shared reference &T promises that the data will not change while the reference is alive. A mutable reference &mut T promises that nobody else can look at the data while you change it. Holding one of each at once breaks both promises, so the compiler refuses. The classic example is a vector: if you hold &items[0] and call items.push(4), the push may need more room, allocate a bigger buffer, copy everything over and free the old one. Your reference then points into freed memory. The rule that prevents the bug is the same one that produced the error.
Form one: a reference to an element, then a push
fn main() {
let mut items = vec![1, 2, 3];
let first = &items[0];
items.push(4);
println!("first is {first}");
}error[E0502]: cannot borrow `items` as mutable because it is also borrowed as immutable
--> e0502a.rs:4:5
|
3 | let first = &items[0];
| ----- immutable borrow occurs here
4 | items.push(4);
| ^^^^^^^^^^^^^ mutable borrow occurs here
5 | println!("first is {first}");
| ----- immutable borrow later used hereThe message lists the three lines that matter, in order. Line 3 starts the shared borrow. Line 4 is the mutation that conflicts. Line 5 is the last use of the borrow, and that is what keeps it alive across line 4. If first were not used after the push, the program would compile, because Rust ends a borrow at its last use, not at the end of the scope.
That observation gives the cheapest fix: do not keep the reference. If you only need the value, copy it out, which ends the borrow at the semicolon:
fn main() {
let mut items = vec![1, 2, 3];
let first = items[0]; // copy the number out; the borrow ends at the semicolon
items.push(4);
println!("first is {first}, now {items:?}");
}first is 1, now [1, 2, 3, 4]This works because i32 is Copy. For a String or another type that is not Copy, clone it (items[0].clone()) if you need to keep a value across the mutation, or reorder the code so the use of the reference comes before the push. The message points to the right line to move: the one marked "immutable borrow later used here".
Form two: mutating a collection inside a loop over it
The more common form is a for loop that reads from a collection and writes back to it:
fn main() {
let mut scores = vec![3, 8, 5];
for s in &scores {
if *s > 4 {
scores.push(*s * 2);
}
}
println!("{scores:?}");
}error[E0502]: cannot borrow `scores` as mutable because it is also borrowed as immutable
--> e0502b.rs:5:13
|
3 | for s in &scores {
| - -------
| | |
| | immutable borrow occurs here
| _____| immutable borrow later used here
| |
4 | | if *s > 4 {
5 | | scores.push(*s * 2);
| | ^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
6 | | }
7 | | }
| |_____- this for loop borrows `scores` immutably, preventing mutation within its body
|
= help: consider using an index-based loop instead, or collecting modifications into a separate collectionA for loop over &scores holds an iterator that points into the vector's buffer for the entire loop. A push inside the body could reallocate that buffer and leave the iterator dangling, which is exactly the bug that makes this a compile error. The compiler's help gives two directions, and the second is nearly always better: compute the changes first, then apply them.
fn main() {
let mut scores = vec![3, 8, 5];
let doubled: Vec<i32> = scores.iter().filter(|s| **s > 4).map(|s| s * 2).collect();
scores.extend(doubled);
println!("{scores:?}");
}[3, 8, 5, 16, 10]The first statement reads scores and finishes, producing a separate Vec. The borrow ends there. The second statement mutates scores with no outstanding references. The two phases, read then write, are the general solution to this class of error, and they also make the code easier to reason about, since the loop no longer depends on a collection that changes under it.
The index-based alternative is for i in 0..scores.len() with scores[i] inside, but be careful: if the body pushes, the loop bound is fixed at the start and the new elements are not visited, which may or may not be what you want. Say what you mean with the two-phase version.
The same error with a HashMap
The map version is the one that hides best. get returns a reference into the map's storage, and holding it across an insert has the same hazard as the vector: the insert may rehash and move every entry.
use std::collections::HashMap;
fn main() {
let mut visits: HashMap<String, u32> = HashMap::new();
visits.insert("home".to_string(), 1);
let count = visits.get("home");
visits.insert("about".to_string(), 1);
println!("home was visited {count:?} times");
}error[E0502]: cannot borrow `visits` as mutable because it is also borrowed as immutable
--> e0502c.rs:8:5
|
7 | let count = visits.get("home");
| ------ immutable borrow occurs here
8 | visits.insert("about".to_string(), 1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
9 | println!("home was visited {count:?} times");
| ----- immutable borrow later used hereFor the common read-modify-write pattern, the standard library has an API designed around the rule. The entry method looks up the key and gives back a handle for inserting or updating it, all within one mutable borrow, so there is nothing to conflict with:
for page in ["home", "about", "home", "home"] {
*visits.entry(page.to_string()).or_insert(0) += 1; // look up and update in one borrow
}[("about", 1), ("home", 3)]That is the output after sorting the pairs. If you find yourself writing get, then insert or get_mut in sequence, it is almost always an entry call in disguise.
Other places this error appears
It appears with a struct field and a method: calling self.helper() which takes &mut self while holding a reference to self.items[0]. The fix is to do the read first, copy out what you need, or split the borrow by working on fields directly, since self.items and self.total are disjoint when you name them, even though two &mut self methods are not. It appears with string slices: let word = &text[0..3]; text.push_str("x"); fails because word borrows from text. And it appears with Option<&T> returned from lookups and held in a variable, which is the get case above in other clothes.
When the reference must stay alive
Sometimes you do need to read and write the same collection in one pass. You have four honest options. Copy or clone the pieces you need before mutating. Collect changes in a side vector and apply them after the loop, as above. Use interior mutability, Cell for Copy values or RefCell for the rest, which moves the check to run time: the same rule is enforced and violations become panics. Or change the data structure: an arena with indices instead of references, so that you hold a usize and not a borrow. The index approach is the standard answer for graphs and trees, where nodes need to refer to each other and be modified.
How to avoid it
- Keep borrows short. Read the value you need and stop holding the reference.
- Prefer functions that take the data they need by value or by
&, and return new data, over ones that update in place while the caller holds references. - Use
retain,drain,extendandentryfor in-place edits; they are written to avoid the conflict. - When a message mentions "later used here", move that later use above the mutation.
The mirror-image error is E0499, two mutable borrows, and the original one is E0382, use of a moved value. For the mental model that makes all three predictable, read ownership in Rust explained.


