Skip to content

Rust Error E0594: Cannot Assign, as It Is Not Declared as Mutable

E0594 means you wrote something = value (or +=, -=, …), but you only have permission to read that something. Usually it’s a struct field on a variable without mut, or a value you reach through a & reference. It’s a very common error, and the fix is almost always one word: mut. This page shows the fix first, then explains it. For the other errors in this series, see Rust compiler errors.

error[E0594]: cannot assign to `p.hp`, as `p` is not declared as mutable
--> src/main.rs:8:5
|
8 | p.hp = 5; // ❌ `p` is read-only
| ^^^^^^^^ cannot assign
|
help: consider changing this to be mutable
|
7 | let mut p = Player { name: String::from("Ferris"), hp: 10 };
| +++

This is the program that produces it:

struct Player {
name: String,
hp: u32,
}
fn main() {
let p = Player { name: String::from("Ferris"), hp: 10 };
p.hp = 5; // ❌ `p` is read-only
println!("{} has {} hp", p.name, p.hp);
}

You will also often see a second wording: “cannot assign to *x, which is behind a & reference”. Same error code, slightly different cause: there you hold a & reference, and a & reference only lets you look. Both are covered below.

Look at the ^^^^^^^^ under p.hp = 5 on line 8. You are changing a field of p. But p was created with plain let, so the whole struct is read-only, and that includes every field inside it.

The help: line shows the fix: let mut p. The +++ marks the three characters to add.

With the second wording, the compiler points at a line like *x += 1 and adds `x` is a `&` reference, so it cannot be written to. You are not changing a variable you own. You are trying to change someone else’s value through a borrowed, read-only reference.

Think of a library book. You can read it as much as you like, but you’re not allowed to write in it. To write notes, you need your own copy (let mut) or the owner’s permission (&mut).

In Rust, everything is read-only unless you say otherwise. A variable needs let mut to change, and a reference needs to be &mut to change what it points to. See Making a struct mutable and Mutable references.

There is no “mutable field” in Rust. Mutability belongs to the variable (or the reference), not to single fields. So let mut p makes every field of p changeable, and plain let p makes none of them changeable. That keeps the rule simple: look at how a value was declared and you know whether it can change.

The reference rule protects you from surprises. If you pass &player to a function, its signature promises it will only read. Without that promise, any function could quietly change your data, and the compiler couldn’t stop a reader and a writer from touching the same value at the same time (that’s what E0502 is about).

Choose this when you own the struct and want to change its fields. This is the right fix most of the time.

struct Player {
name: String,
hp: u32,
}
fn main() {
let mut p = Player { name: String::from("Ferris"), hp: 10 };
p.hp = 5;
p.hp += 3;
println!("{} has {} hp", p.name, p.hp);
}

Output:

Ferris has 8 hp
▶ Try it Yourself

The same goes for tuples (pair.0 = 1;) and arrays (grid[2] = 7;): the variable holding them needs mut. For a struct inside a Vec, team[0].hp = 10; needs let mut team; that case is reported as E0596, but the fix is the same.

If you only assign once, you may not need mut at all: build the struct with the right value from the start, like Player { hp: 5, ..other }.

Choose this when you loop over a collection and want to change each item. for x in &v and v.iter() hand out & references, which are read-only:

fn main() {
let scores = vec![10, 20, 30];
for x in &scores {
*x += 1; // ❌ `x` is a `&i32`
}
println!("{scores:?}");
}
error[E0594]: cannot assign to `*x`, which is behind a `&` reference
--> src/main.rs:4:9
|
3 | for x in &scores {
| ------- this iterator yields `&` references
4 | *x += 1; // ❌ `x` is a `&i32`
| ^^^^^^^ `x` is a `&` reference, so it cannot be written to
|
help: use a mutable iterator instead
|
3 | for x in &mut scores {
| +++

Loop over &mut scores (or call .iter_mut()), and make the vector itself mut:

fn main() {
let mut scores = vec![10, 20, 30];
for x in &mut scores {
*x += 1;
}
println!("{scores:?}");
for x in scores.iter_mut() {
*x *= 2;
}
println!("{scores:?}");
}

Output:

[11, 21, 31]
[22, 42, 62]
▶ Try it Yourself

&mut scores and scores.iter_mut() do the same thing; pick whichever reads better. If you don’t need to change the list in place, building a new one also works: let bigger: Vec<i32> = scores.iter().map(|x| x + 1).collect();. See Iterators.

Choose this when a method or function changes data it was given. A method with &self can only read the struct, so assigning to a field fails:

impl Account {
fn deposit(&self, amount: u64) {
self.balance += amount; // ❌ `self` is a `&` reference
}
}
error[E0594]: cannot assign to `self.balance`, which is behind a `&` reference
--> src/main.rs:7:9
|
7 | self.balance += amount;
| ^^^^^^^^^^^^^^^^^^^^^^ `self` is a `&` reference, so it cannot be written to
|
help: consider changing this to be a mutable reference
|
6 | fn deposit(&mut self, amount: u64) {
| +++

A plain function with a & parameter gives the same error: fn reset(score: &u32) { *score = 0; } fails with cannot assign to `*score`, which is behind a `&` reference. The fix is the same for both: say &mut in the signature, make the caller’s variable let mut, and pass &mut at the call.

struct Account {
balance: u64,
}
impl Account {
fn deposit(&mut self, amount: u64) {
self.balance += amount;
}
}
fn reset(score: &mut u32) {
*score = 0;
}
fn main() {
let mut acc = Account { balance: 0 };
acc.deposit(50);
acc.deposit(25);
println!("Balance: {}", acc.balance);
let mut score = 42;
reset(&mut score);
println!("Score: {score}");
}

Output:

Balance: 75
Score: 0
▶ Try it Yourself

For methods, Rust writes the &mut for you at the call: acc.deposit(50) borrows acc mutably on its own, as long as acc is let mut. For functions you write it yourself: reset(&mut score). If you pass &score to a function that wants &mut u32, you get E0308 (types differ in mutability). See Adding behavior with methods.

Choose this only when you can’t get &mut: for example, you implement a trait whose method is fixed as &self, or many parts of the program share one value through Rc. Cell and RefCell allow changes through a & reference (“interior mutability”):

use std::cell::{Cell, RefCell};
struct Catalog {
items: Vec<String>,
lookups: Cell<u32>,
history: RefCell<Vec<String>>,
}
impl Catalog {
fn contains(&self, name: &str) -> bool {
self.lookups.set(self.lookups.get() + 1);
self.history.borrow_mut().push(name.to_string());
self.items.iter().any(|item| item == name)
}
}
fn main() {
let catalog = Catalog {
items: vec![String::from("apple"), String::from("pear")],
lookups: Cell::new(0),
history: RefCell::new(Vec::new()),
};
println!("pear? {}", catalog.contains("pear"));
println!("kiwi? {}", catalog.contains("kiwi"));
println!("Lookups: {}", catalog.lookups.get());
println!("History: {:?}", catalog.history.borrow());
}

Output:

pear? true
kiwi? false
Lookups: 2
History: ["pear", "kiwi"]
▶ Try it Yourself

Cell is for small Copy values like counters; RefCell is for anything else. Be honest about the cost: RefCell moves the borrow check from compile time to run time, and it panics if you borrow mutably twice at once. If plain &mut works, prefer it. Across threads, use Mutex or atomics instead (see Shared state). More in RefCell.

Changing a value inside a HashMap. *stock.get("apple").unwrap() += 1; fails with cannot assign to data in a `&` reference, because get returns a & reference. Fix: *stock.get_mut("apple").unwrap() += 1;, or *stock.entry("apple").or_insert(0) += 1;. See HashMaps.

A closure passed as Fn that changes a variable. run(|| count += 1) where run takes F: Fn() fails with cannot assign to `count`, as it is a captured variable in a `Fn` closure. Fix: change the bound to F: FnMut() (and mut f inside run). See When you pick the wrong bound.

A global counter in a static. static VISITS: u32 = 0; then VISITS += 1; fails with cannot assign to immutable static item `VISITS`. Fix: use static VISITS: AtomicU32 = AtomicU32::new(0); and VISITS.fetch_add(1, Ordering::Relaxed);, or better, pass a counter around as &mut.

  • E0596: cannot borrow as mutable. The same missing mut, but for a method call like list.push(x) instead of an assignment.
  • E0384: cannot assign twice to immutable variable. You assign to a whole variable (x = 5;) instead of a field or through a reference.
  • E0308: mismatched types, types differ in mutability, when you pass &x where &mut x is expected.
  • E0502: you have &mut access, but something else is still reading the value.
  • E0507: cannot move out of a value behind a reference. The “read-only” rule again, this time for moves instead of writes.
  • All compiler errors: the full list of error pages.

Quick check

1. let user = User { age: 30 }; user.age = 31; gives E0594. What is the fix?

2. for p in &prices { *p += 1; } fails. What do you change?

3. A method fn rename(&self, n: String) { self.name = n; } gives E0594. What is the usual fix?

Score: 0 / 3