Introduction

Error E0502 is one of the most common borrow checker errors in Rust. It occurs when your code attempts to mutably borrow a value while an immutable borrow to that same value is still active. The Rust compiler prevents this to guarantee memory safety without a garbage collector.

Symptoms

  • Compiler error E0502: cannot borrow as mutable because it is also borrowed as immutable
  • Error points to two lines: the immutable borrow and the mutable borrow
  • Common in loops that read from and write to the same collection
  • Occurs when a reference is held across a function call that needs mutation
  • Error persists even after narrowing scope in older Rust editions

Common Causes

  • Holding a reference to a collection element while modifying the collection
  • Iterator holds an immutable borrow; calling a mutating method on the same data
  • Struct field reference held while mutating another field of the same struct
  • Return value holds a reference that outlives the intended scope

Step-by-Step Fix

  1. 1.Limit the scope of the immutable borrow:
  2. 2.```rust
  3. 3.// Before: borrow spans the entire function
  4. 4.let max_score = scores.iter().max().unwrap();
  5. 5.scores.push(100); // Error!
  6. 6.println!("Max was: {}", max_score);

// After: immutable borrow ends before mutable borrow let max_score = scores.iter().max().copied().unwrap_or(0); scores.push(100); // OK: max_score is a copy, not a reference println!("Max was: {}", max_score); ```

  1. 1.Use indices instead of references in loops:
  2. 2.```rust
  3. 3.// Before: holds references while mutating
  4. 4.for item in items.iter() {
  5. 5.if item.should_duplicate() {
  6. 6.items.push(item.clone()); // Error!
  7. 7.}
  8. 8.}

// After: collect first, then mutate let to_add: Vec<_> = items.iter() .filter(|item| item.should_duplicate()) .cloned() .collect(); items.extend(to_add); // OK: immutable borrow ended ```

  1. 1.Use separate variables to avoid overlapping borrows:
  2. 2.```rust
  3. 3.// Before
  4. 4.let name = &user.name;
  5. 5.user.update_name("new"); // Error: mutable borrow while immutable active
  6. 6.println!("Old name: {}", name);

// After: extract the value, don't hold a reference let name = user.name.clone(); user.update_name("new"); // OK println!("Old name: {}", name); ```

  1. 1.**Use RefCell for runtime borrow checking when needed**:
  2. 2.```rust
  3. 3.use std::cell::RefCell;

struct Cache { data: RefCell<Vec<String>>, }

impl Cache { fn add_and_read(&self) { // Runtime borrow checking instead of compile-time self.data.borrow_mut().push("new item".to_string()); let len = self.data.borrow().len(); println!("Cache size: {}", len); } } ```

Prevention

  • Prefer .copied() or .cloned() to convert references to owned values
  • Use split_at_mut() or split_first_mut() for disjoint mutable access
  • Restructure code to minimize the lifetime of references
  • Use indices (for i in 0..vec.len()) instead of iterators when mutation is needed
  • Consider whether the data structure can be redesigned to avoid shared mutation
  • Use cell module types (Cell, RefCell) for interior mutability when unavoidable