Rust Error E0596: Cannot Borrow as Mutable, as It Is Not Declared as Mutable
E0596 means some code wants to change a value (it needs a mutable borrow, &mut), but you only gave it permission to read. Usually you forgot mut on a let, or a function takes & where it needs &mut. It is very common, and the compiler’s help: line almost always tells you exactly where to add mut. This page shows the fix first, then explains it. For the other errors in this series, see Rust compiler errors.
The error message
Section titled “The error message”error[E0596]: cannot borrow `tasks` as mutable, as it is not declared as mutable --> src/main.rs:3:5 |3 | tasks.push("write tests"); // ❌ push needs to change `tasks` | ^^^^^ cannot borrow as mutable |help: consider changing this to be mutable |2 | let mut tasks = Vec::new(); | +++This is the program that produces it:
fn main() { let tasks = Vec::new(); tasks.push("write tests"); // ❌ push needs to change `tasks` println!("{tasks:?}");}You will also see a second wording: “cannot borrow *tasks as mutable, as it is behind a & reference”. Same error code, slightly different cause: there the variable is a & reference, so you can’t change what it points to. Both are covered below.
What it means
Section titled “What it means”Look at the ^^^^^ under tasks on line 3. To add an item, Vec::push needs a mutable borrow of the vector: its signature is fn push(&mut self, value: T). But tasks was created with plain let, so it is read-only. Rust can’t hand out a &mut to something you said would never change.
The help: line shows the fix: let mut tasks. The +++ marks the three characters to add.
Think of a shared document with “view only” access. You can read every word, but the “edit” button is greyed out. mut (or &mut) is how you ask for edit access.
Why Rust does this
Section titled “Why Rust does this”In Rust, variables are immutable by default, and references are read-only by default. Changing anything needs an explicit opt-in: let mut for a variable you own, &mut for a reference to something you borrowed. See Variables and Borrowing.
This makes code easier to trust. If a function takes &Vec<String>, you know just from its signature that it won’t change your list. If it takes &mut Vec<String>, you know it might. Nothing changes behind your back.
The same rule makes Rust’s bigger safety guarantees possible. Because “can change” is always written down, the compiler can make sure there is never a writer and a reader at the same time (that’s E0502 and E0499). E0596 is the first step: you have to ask to write before anyone checks whether writing is safe.
How to fix it
Section titled “How to fix it”Fix 1: Add mut to the variable
Section titled “Fix 1: Add mut to the variable”Choose this when you own the value and it should change: a Vec you push to, a String you append to, or a struct whose &mut self method you call. This is the right fix most of the time.
struct Counter { count: u32,}
impl Counter { fn increment(&mut self) { self.count += 1; }}
fn main() { let mut tasks = Vec::new(); tasks.push("write tests"); tasks.push("fix bug"); println!("{tasks:?}");
let mut counter = Counter { count: 0 }; counter.increment(); counter.increment(); println!("Count: {}", counter.count);}Output:
["write tests", "fix bug"]Count: 2Without mut on counter, counter.increment() fails with cannot borrow counter as mutable. Calling a method that takes &mut self borrows the whole variable mutably, so the variable must be mut. Methods that only take &self work fine without it. See Adding behavior with methods.
The same goes for a parameter that a function owns: fn shout(s: String) can’t call s.push('!'), but fn shout(mut s: String) can.
Fix 2: Change the parameter to &mut and pass &mut x
Section titled “Fix 2: Change the parameter to &mut and pass &mut x”Choose this when a function needs to change something the caller owns. This version gives the “behind a & reference” wording:
fn add_task(tasks: &Vec<String>, task: &str) { tasks.push(task.to_string()); // ❌ `tasks` is a `&` reference}error[E0596]: cannot borrow `*tasks` as mutable, as it is behind a `&` reference --> src/main.rs:2:5 |2 | tasks.push(task.to_string()); // ❌ `tasks` is a `&` reference | ^^^^^ `tasks` is a `&` reference, so it cannot be borrowed as mutable |help: consider changing this to be a mutable reference |1 | fn add_task(tasks: &mut Vec<String>, task: &str) { | +++Fix it in three places: the parameter type becomes &mut Vec<String>, the caller’s variable becomes let mut, and the call passes &mut tasks:
fn add_task(tasks: &mut Vec<String>, task: &str) { tasks.push(task.to_string());}
fn main() { let mut tasks = Vec::new(); add_task(&mut tasks, "write tests"); add_task(&mut tasks, "update docs"); println!("{} tasks: {tasks:?}", tasks.len());}Output:
2 tasks: ["write tests", "update docs"]If you change the parameter but still pass &tasks, you get E0308 instead, with the note types differ in mutability. That just means: write &mut tasks at the call too. Writing &mut at the call site is on purpose; anyone reading main can see that add_task may change tasks.
Fix 3: Make the closure binding mut
Section titled “Fix 3: Make the closure binding mut”Choose this when a closure changes a variable it captured. Such a closure changes its own state each time it runs, so calling it needs a mutable borrow of the closure itself:
fn main() { let mut clicks = 0; let click = || clicks += 1; click(); // ❌ cannot borrow `click` as mutable click(); println!("Clicks: {clicks}");}error[E0596]: cannot borrow `click` as mutable, as it is not declared as mutable --> src/main.rs:3:9 |3 | let click = || clicks += 1; | ^^^^^ ------ calling `click` requires mutable binding due to mutable borrow of `clicks` | | | not mutable4 | click(); // ❌ cannot borrow `click` as mutable | ----- cannot borrow as mutable5 | click(); | ----- cannot borrow as mutable |help: consider changing this to be mutable |3 | let mut click = || clicks += 1; | +++The variable clicks is already mut. What’s missing is mut on the closure:
fn main() { let mut clicks = 0; let mut click = || clicks += 1; click(); click(); click(); println!("Clicks: {clicks}");}Output:
Clicks: 3This surprises many people, because the closure doesn’t “look” like it changes. But it holds a &mut clicks inside, so it’s an FnMut closure, and calling an FnMut needs mut. See Closures that change captured variables and Fn, FnMut and FnOnce.
Fix 4: Use iter_mut() or &mut in a loop
Section titled “Fix 4: Use iter_mut() or &mut in a loop”Choose this when you loop over a collection and want to change each item. .iter() and for x in &list hand out & references, which are read-only:
for name in names.iter() { name.push('!'); // ❌ cannot borrow `*name` as mutable, as it is behind a `&` reference}Switch to .iter_mut() or &mut names (and make names itself mut):
fn main() { let mut names = vec![String::from("ferris"), String::from("corro")]; for name in names.iter_mut() { name.push('!'); } println!("{names:?}");
let mut prices = vec![10, 20, 30]; for price in &mut prices { *price += 5; } println!("{prices:?}");}Output:
["ferris!", "corro!"][15, 25, 35]The compiler suggests this too: help: use mutable method ... names.iter_mut(). The same applies to closures in iterator chains: words.iter().for_each(|w| w.push('!')) fails, words.iter_mut().for_each(|w| w.push('!')) works. See Iterators.
Where you’ll usually see it
Section titled “Where you’ll usually see it”Building a Vec or String step by step. let list = Vec::new(); then list.push(...), or let s = String::new(); then s.push_str(...). Fix: let mut. If you have all the items up front, let list = vec![a, b, c]; needs no mut at all.
Calling a “setter” or “update” method on a struct. account.deposit(50) where deposit takes &mut self fails unless you wrote let mut account. Fix: let mut account = Account::new();. See Structs.
Helper functions that take & out of habit. You write fn reset(board: &Board) and then try to change board inside. Fix: change it to board: &mut Board and call reset(&mut board). A good habit: write & when the function only reads, &mut when it changes.
Changing a value inside a HashMap. if let Some(v) = map.get("key") { v.push(..) } fails, because get returns a & reference. Fix: use map.get_mut("key"), or the entry API. See HashMaps.
Related errors
Section titled “Related errors”- E0384: cannot assign twice to immutable variable. The same missing
mut, but forx = ...orx += 1instead of a method call. - E0594: cannot assign to a field or through a
&reference, for examplep.hp = 5;whenpisn’tmut, or*x += 1infor x in &v. Same fixes:let mut, or&mut. - E0308: mismatched types,
types differ in mutability, when you pass&xto a function that wants&mut x. - E0502: you got
&mutaccess, but something else is still reading the value. - E0499: two
&mutborrows of the same value at the same time. - All compiler errors: the full list of error pages.
Quick check
Section titled “Quick check”Quick check
1. let scores = Vec::new(); scores.push(10); gives E0596. What is the fix?
2. A function fn add(list: &Vec<u32>) calls list.push(1). What do you change?
3. let mut total = 0; let add = |x| total += x; add(5); fails. Why?
Score: 0 / 3
Read more
Section titled “Read more”- E0596 — Rust error codes index: the official explanation of this error.
- References and Borrowing — The Rust Book: the “Mutable References” part explains
&mutand why it must be asked for. - Method Syntax — The Rust Book:
&selfversus&mut self, and what each one needs from the caller. - Closures — The Rust Book: how closures capture variables, and why a changing closure must be
mut. - Mutability (borrowing) — Rust by Example: runnable examples of
&and&mutborrows side by side.