The Error
Two mutable references to the same variable, both alive at once. The Rust compiler sees it immediately and refuses to build:
error[E0499]: cannot borrow `vec` as mutable more than once at a time
--> src/main.rs:5:18
|
4 | let first = &mut vec;
| -------- first mutable borrow occurs here
5 | let second = &mut vec;
| ^^^^^^^^ second mutable borrow occurs here
6 | println!("{}", first[0]);
| ----- first borrow later used here
This is the borrow checker doing its job. The rule is strict: one mutable reference at a time, no exceptions.
Why This Happens
Rust prevents data races at compile time. Two simultaneous mutable references to the same data means one could modify or invalidate it while the other is reading โ a classic bug in C/C++ that Rust makes structurally impossible.
The tricky part: "at the same time" doesn't mean what you'd expect. A mutable borrow stays alive until its last use, not until you assign the next variable. This trips up a lot of developers coming from other languages.
Step-by-Step Fix
Step 1: Identify the overlapping borrows
Run rustc --explain E0499 to get the compiler's full explanation. The error output also shows you exactly where each borrow starts and where it's still in use. Your goal: make sure the first mutable borrow is completely done before the second one starts.
Step 2: Let the first borrow go out of scope
The simplest fix โ use the first reference completely, then create the second.
fn main() {
let mut vec = vec![1, 2, 3];
// BAD: both borrows alive at the same time
// let first = &mut vec;
// let second = &mut vec; // error!
// GOOD: first borrow is done before second begins
{
let first = &mut vec;
first.push(4);
} // first borrow ends here
let second = &mut vec;
second.push(5);
println!("{:?}", vec); // [1, 2, 3, 4, 5]
}
Step 3: Use indices instead of multiple references (for collections)
Trying to mutably reference two elements of the same Vec at once is one of the most frequent triggers for E0499. Index-based access sidesteps the problem entirely:
fn main() {
let mut data = vec![10, 20, 30];
// BAD: two mutable references into the same Vec
// let a = &mut data[0];
// let b = &mut data[1]; // error!
// GOOD: use indices directly
data[0] += data[1]; // reads [1] and writes [0] in one statement
println!("{:?}", data); // [30, 20, 30]
// Or: split the slice
let (left, right) = data.split_at_mut(1);
left[0] = right[0] + right[1];
println!("{:?}", data); // [50, 20, 30]
}
Step 4: Use split_at_mut for disjoint slice mutation
Need two mutable views into different parts of the same collection? split_at_mut divides a slice into two non-overlapping mutable halves. The borrow checker accepts this because the two halves provably can't alias:
fn swap_first_last(data: &mut Vec<i32>) {
let len = data.len();
if len < 2 {
return;
}
let (head, tail) = data.split_at_mut(len - 1);
std::mem::swap(&mut head[0], &mut tail[0]);
}
fn main() {
let mut v = vec![1, 2, 3, 4, 5];
swap_first_last(&mut v);
println!("{:?}", v); // [5, 2, 3, 4, 1]
}
Step 5: Restructure logic into separate passes
Sometimes the design itself is the problem. Reading from and writing to the same structure simultaneously is inherently conflicting โ break it into two distinct passes instead:
fn main() {
let mut scores: Vec<i32> = vec![3, 7, 2, 9, 1];
// BAD pattern: trying to find max and update in one pass
// with multiple mutable borrows
// GOOD: two separate passes
let max = *scores.iter().max().unwrap(); // read pass (immutable borrow)
for s in scores.iter_mut() { // write pass (mutable borrow)
*s = if *s == max { 100 } else { *s };
}
println!("{:?}", scores); // [3, 7, 2, 100, 1]
}
Step 6: Use RefCell for interior mutability (when needed)
Sometimes the borrow checker is too conservative โ graph-like structures and certain recursive patterns are common examples. RefCell<T> is the escape hatch: it defers the borrow check to runtime instead of compile time.
use std::cell::RefCell;
fn main() {
let data = RefCell::new(vec![1, 2, 3]);
{
let mut borrow = data.borrow_mut();
borrow.push(4);
} // mutable borrow released here
println!("{:?}", data.borrow()); // [1, 2, 3, 4]
}
Warning: RefCell panics at runtime if you actually violate the borrow rules. Treat it as a last resort โ restructuring the code is almost always the cleaner solution.
Verify the Fix
Build the project and confirm E0499 is gone:
cargo build
Then run your tests to make sure nothing broke:
cargo test
Also worth running cargo clippy โ it catches patterns that tend to cause this error before you even hit the compiler:
cargo clippy
Quick Reference: Which Fix to Use
- Borrows don't actually overlap? Add a block
{ }to scope the first borrow. - Two elements of a Vec? Use
split_at_mut()or index arithmetic. - Read then write pattern? Separate into two passes.
- Complex shared state? Use
RefCell<T>(single-threaded) orMutex<T>(multi-threaded). - Graph / tree structures? Consider arena crates like
slotmaporgenerational-arena.
Common Mistakes
- Assuming the borrow ends at the assignment line. It doesn't. A borrow lives until its last use. NLL (Non-Lexical Lifetimes), introduced in Rust 2018, handles many obvious cases automatically โ but complex patterns still catch people off guard.
- Reaching for
RefCellfirst. It's a last resort, not a convenience. Restructuring the code is almost always the right call. - Calling a
&mut selfmethod while holding a reference to a field. That method call borrows the entire struct mutably. Any existing reference to any field of that struct will conflict โ even if the method never touches that particular field.

