Fix error[E0502]: cannot borrow as mutable because it is also borrowed as immutable in Rust

intermediate๐Ÿฆ€ Rust2026-03-17| Rust 1.x (all versions), any OS โ€” Linux, macOS, Windows

Error Message

error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
#rust#borrow#ownership#lifetime

TL;DR

You're holding an immutable reference (&x) while also trying to mutate x via &mut x. Rust won't allow both at once. End the immutable borrow before the mutable one begins. In practice that means restructuring code to avoid overlapping references โ€” or cloning the data you need upfront.

The error in full

error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
 --> src/main.rs:6:5
  |
4 |     let r = &x;          // immutable borrow starts here
5 |     println!("{}", r);
6 |     x.push(4);           // mutable borrow attempted here
  |     ^^^^^^^^^ mutable borrow occurs here
7 |     println!("{}", r);   // immutable borrow later used here
  |                    - immutable borrow later used here

The borrow checker enforces one fundamental rule: either multiple immutable references, or one mutable reference โ€” never both simultaneously. This isn't an arbitrary restriction. It's the mechanism that gives Rust memory safety without a garbage collector.

Root cause

The moment you write let r = &x, Rust considers x immutably borrowed for as long as r is in use. Calling x.push(4) then creates a conflict โ€” one part of the code holds a read-only view while another tries to change the underlying data. In C++ or Java, this silently produces iterator invalidation or a data race. Rust refuses to compile it.

Borrows last until the last use of the reference โ€” not until the end of the block. That rule is called Non-Lexical Lifetimes (NLL), stabilized in Rust 2018. It helps a lot, but it can't save you if you actually use the immutable reference after the mutation.

Fix approach 1 โ€” Stop using the immutable reference before mutating

The most straightforward fix: restructure the code so the immutable borrow ends before the mutation happens.

fn main() {
    let mut x = vec![1, 2, 3];

    // Use the reference, then let it drop
    {
        let r = &x;
        println!("before: {:?}", r);
    } // <-- r goes out of scope here

    x.push(4); // now safe to mutate
    println!("after: {:?}", x);
}

No extra block needed if you just don't use r after the mutation โ€” NLL automatically ends the borrow at the last use:

fn main() {
    let mut x = vec![1, 2, 3];
    let r = &x;
    println!("before: {:?}", r);
    // r is not used after this line, so the borrow ends here (NLL)

    x.push(4);
    println!("after: {:?}", x);
}

Fix approach 2 โ€” Clone the data you need before mutating

Sometimes you genuinely need the old value after the mutation. Clone it upfront โ€” an owned copy has no borrow on x.

fn main() {
    let mut x = vec![1, 2, 3];
    let snapshot = x.clone(); // owned copy, no borrow of x

    x.push(4);

    println!("was: {:?}", snapshot);
    println!("now: {:?}", x);
}

Fix approach 3 โ€” Use indices instead of references

Vec-based code hits E0502 constantly: grab a reference to one element, then try to push another. The compiler blocks it โ€” even when the push would never touch the element you referenced. Indices sidestep the problem entirely.

// Bad: storing a reference into the vec
fn bad_example(items: &mut Vec<String>) {
    let first = &items[0];   // immutable borrow
    items.push("new".to_string()); // mutable borrow โ€” COMPILE ERROR
    println!("{}", first);
}

// Good: store the index (or a clone), not the reference
fn good_example(items: &mut Vec<String>) {
    let first = items[0].clone(); // owned String, no borrow held
    items.push("new".to_string()); // fine
    println!("{}", first);
}

Fix approach 4 โ€” Interior mutability with RefCell (single-threaded)

Graph structures, caches, and event systems sometimes need shared mutable access that the static borrow checker simply can't reason about. RefCell<T> handles this by moving the borrow check from compile time to runtime.

use std::cell::RefCell;

fn main() {
    let x = RefCell::new(vec![1, 2, 3]);

    let r = x.borrow(); // immutable borrow (runtime)
    println!("before: {:?}", *r);
    drop(r);            // explicitly release before mutating

    x.borrow_mut().push(4); // now fine
    println!("after: {:?}", x.borrow());
}

One caveat: if two borrows conflict at runtime, RefCell panics rather than causing undefined behaviour. For multi-threaded code, reach for Arc<Mutex<T>> instead.

Fix approach 5 โ€” Split the struct into separate fields

E0502 inside a struct method usually means Rust can't see that two fields are independent. Borrow fields individually rather than self as a whole.

struct Game {
    players: Vec<String>,
    log: Vec<String>,
}

impl Game {
    fn record_join(&mut self, name: &str) {
        // Borrow two separate fields โ€” this compiles fine
        let players = &self.players;
        let entry = format!("joined: {}, total: {}", name, players.len());
        self.log.push(entry); // mutates log, not players โ€” OK
    }
}

Borrowing &self as a whole and then requesting &mut self looks like a conflict to the compiler, even when the actual fields don't overlap. Reaching directly for self.players and self.log makes the independence explicit.

Verifying the fix

Rust's error messages double as your test suite. Start here:

cargo check

No E0502 means you're clear. Then confirm behaviour hasn't changed:

cargo test

Added a .clone()? Run Clippy too:

cargo clippy

Clippy flags unnecessary clones. On large vecs or strings, a redundant clone can quietly become a serious performance issue.

Quick decision guide

  • References don't actually overlap at runtime? โ†’ Restructure so the immutable borrow ends first (approach 1).
  • Need a snapshot of the old value? โ†’ Clone it (approach 2).
  • Indexing a collection you're also modifying? โ†’ Store the index, not a reference (approach 3).
  • Shared ownership or graph structures? โ†’ RefCell or Arc<Mutex> (approach 4).
  • Conflict inside a struct method? โ†’ Borrow individual fields (approach 5).

Further reading

Related Error Notes