Rust Error E0384: Cannot Assign Twice to Immutable Variable
E0384 means you gave a variable a value and then tried to give it another value, but the variable was not declared with mut. In Rust, variables can’t be changed unless you say so. Almost everyone hits this in their first hour of Rust, and the fix is usually three letters: mut. This page shows the fix first, then explains why Rust works this way. For the other errors in this series, see Rust compiler errors.
The error message
Section titled “The error message”error[E0384]: cannot assign twice to immutable variable `score` --> src/main.rs:4:5 |2 | let score = 10; | ----- first assignment to `score`3 | println!("Score: {score}");4 | score = 15; // ❌ score is not mut | ^^^^^^^^^^ cannot assign twice to immutable variable |help: consider making this binding mutable |2 | let mut score = 10; | +++This is the program that produces it:
fn main() { let score = 10; println!("Score: {score}"); score = 15; // ❌ score is not mut println!("New score: {score}");}If the variable is a function parameter, the wording changes a little to “cannot assign to immutable argument”. It is the same error code and the same idea.
What it means
Section titled “What it means”The compiler points at two places:
- Line 2, marked
first assignment to score: this is wherescoregot its value. Because the line saysletand notlet mut, that value is final. - Line 4, marked with
^^^^: this is where you tried to put a new value in. Rust refuses.
The help: part already shows the fix: let mut score = 10;. The +++ marks the three characters to add.
Think of let as writing in pen and let mut as writing in pencil. Both are fine. But if you wrote in pen, you can’t erase it later.
The same rule applies to +=, -=, *= and friends. count += 1 is an assignment too, so it also needs mut.
Why Rust does this
Section titled “Why Rust does this”In Rust, every variable is immutable by default. You have to opt in to change with mut. This is explained on the Variables page.
This sounds strict, but it helps you a lot. When you read let total = ..., you know total keeps that value until the end of its scope. You don’t have to scan the rest of the function to check if something changed it. When you do see let mut, it’s a clear signal: “watch this one, it changes”.
It also catches real bugs. If you meant to change a copy but accidentally wrote to the original, or you reused a name by mistake, the compiler stops you. And Rust warns you about mut variables that never change (variable does not need to be mutable), so the signal stays honest in both directions.
How to fix it
Section titled “How to fix it”Fix 1: Add mut
Section titled “Fix 1: Add mut”Choose this when the variable really should change over time: a counter, a running total, a score, a state. This is the right fix most of the time, and it’s exactly what the compiler suggests.
fn main() { let mut score = 10; println!("Score: {score}"); score = 15; // ✅ allowed now println!("New score: {score}");
let mut count = 0; for _ in 0..3 { count += 1; } println!("Count: {count}");}Output:
Score: 10New score: 15Count: 3The counter is the classic case: let count = 0; followed by count += 1; inside a loop gives E0384 on the += line. Adding mut fixes it. See Making a variable changeable with mut.
Fix 2: Use shadowing for a new value
Section titled “Fix 2: Use shadowing for a new value”Choose this when you don’t really want to change the variable. You want a new value that is computed from the old one, often in a few steps, and after that it stays the same. Write let again with the same name:
fn main() { let price = 20; let price = price + 5; // a new `price`, built from the old one let price = price * 2; println!("Final price: {price}");
let input = " 42 "; let input = input.trim(); // still text let input: u32 = input.parse().unwrap(); // now a number println!("Parsed: {}", input + 1);}Output:
Final price: 50Parsed: 43This is called shadowing: each let creates a brand-new variable that hides the old one. Two nice things come with it. The final price is immutable, so nothing can change it by accident later. And the type can change, like input going from &str to u32, which mut can’t do. More on this in Shadowing: reusing the same name.
A simple rule: if the value changes again and again (in a loop, over time), use mut. If you’re preparing a value in a few steps, shadowing reads nicely.
Fix 3: Add mut to a function parameter
Section titled “Fix 3: Add mut to a function parameter”Choose this when a function wants to change its own copy of an argument. Parameters are immutable too, so this fails:
fn countdown(n: u32) { while n > 0 { println!("{n}..."); n -= 1; // ❌ cannot assign to immutable argument `n` } println!("Liftoff!");}error[E0384]: cannot assign to immutable argument `n` --> src/main.rs:4:9 |4 | n -= 1; // ❌ cannot assign to immutable argument `n` | ^^^^^^ cannot assign to immutable argument |help: consider making this binding mutable |1 | fn countdown(mut n: u32) { | +++Put mut before the parameter name:
fn countdown(mut n: u32) { while n > 0 { println!("{n}..."); n -= 1; } println!("Liftoff!");}
fn main() { let start = 3; countdown(start); println!("start is still {start}");}Output:
3...2...1...Liftoff!start is still 3Notice that start in main didn’t change. mut n only makes the function’s own copy changeable; the caller doesn’t need mut and isn’t affected. If you want the function to change the caller’s variable, take a &mut u32 instead and pass &mut start. See Functions and Borrowing.
Where you’ll usually see it
Section titled “Where you’ll usually see it”Changing a for loop variable. The loop variable is a fresh, immutable variable on every pass:
for i in 0..5 { if i == 2 { i += 1; // ❌ cannot assign twice to immutable variable `i` } println!("{i}");}Fix: you rarely want to change the loop counter itself. Skip ahead with continue, use .step_by(2), or, if you need full control, switch to a while loop with a let mut i. (for mut i in 0..5 compiles, but it only changes this pass’s copy; it doesn’t skip anything.) See break and continue and while loops.
Declaring first, assigning later. let total; without a value is allowed, and you can assign it once. A second assignment gives E0384:
let total;total = 5;total = 6; // ❌ cannot assign twice to immutable variable `total`Fix: if it really changes, use let mut total. If you only meant to pick one value, compute it in one expression: let total = if big_order { 6 } else { 5 };. See if / else.
Accumulating a total. let sum = 0; followed by sum += price; in a loop is the most common E0384 of all. Fix: let mut sum = 0;, or skip the variable and write let sum: u32 = prices.iter().sum();. See Iterators.
Related errors
Section titled “Related errors”- E0596: cannot borrow as mutable, as it is not declared as mutable. The same “you forgot
mut” problem, but for calling a changing method like.push()instead of assigning. - E0594: cannot assign to
p.hp, aspis not declared as mutable. You see this when you assign to a struct field (p.hp = 5;) or write through a&reference. Fix:let mut p, or a&mutreference. See Making a struct mutable. - E0502: you tried to change a value while it was borrowed for reading.
- E0382: you used a value after it was moved.
- All compiler errors: the full list of error pages.
Quick check
Section titled “Quick check”Quick check
1. You write let lives = 3; and later lives -= 1;. Why do you get E0384?
2. You want to clean up name once (trim it and make it uppercase) and never change it again. What reads best?
3. fn halve(n: u32) does n /= 2; and gets E0384. What is the fix that keeps the caller unchanged?
Score: 0 / 3
Read more
Section titled “Read more”- E0384 — Rust error codes index: the official explanation of this error, with an example.
- Variables and Mutability — The Rust Book: the chapter on
let,mut, constants, and shadowing. - Mutability — Rust by Example: a short runnable example of
letversuslet mut. - Freezing — Rust by Example: how shadowing a mutable variable with an immutable one “freezes” it.