How to Fix Rust Error E0384: Cannot Assign Twice to Immutable Variable

beginner🦀 Rust2026-03-30| Rust compiler (rustc) on Windows, macOS, or Linux. Usually encountered during the compilation phase using Cargo.

Error Message

error[E0384]: cannot assign twice to immutable variable `x`
#rust#compiler-errors#programming#backend

Understanding the Error

Rust plays it safe. By default, every variable you create is immutable. This means once you bind a value to a name using the let keyword, that value is locked in. If you try to swap it out for something else later, the compiler will stop you cold.

error[E0384]: cannot assign twice to immutable variable `x`
  --> src/main.rs:4:5
   |
3  |     let x = 5;
   |         - first assignment to `x`
4  |     x = 6;
   |     ^^^^^ cannot assign twice to immutable variable

Think of this strictness as a safety net. It prevents a whole class of bugs where data changes unexpectedly behind your back. While it might feel restrictive at first, Rust gives you several idiomatic ways to work around it.

The Root Cause

The compiler throws E0384 because you are trying to change a value that was promised to be constant. In Rust, immutability is the baseline. Unless you explicitly tell the compiler a variable will change, it assumes the value stays the same for its entire stay in that scope. This allows the compiler to optimize your code more aggressively and makes your logic easier to follow.

Solution 1: Use the mut Keyword

The quickest path to a fix is declaring your variable as mutable. By adding mut, you signal to both the compiler and other developers that this value is meant to evolve.

Example: Tracking a Counter

fn main() {
    // Use 'mut' to allow reassignment
    let mut retry_count = 0;
    println!("Current attempts: {}", retry_count);

    retry_count = 1;
    println!("Updated attempts: {}", retry_count);
}

When to use this: Opt for mut when you have a single piece of data that naturally changes over time. Common examples include loop counters, accumulators, or buffers that receive new data chunks.

Solution 2: Variable Shadowing

Sometimes you don't need a variable to be mutable; you just need to transform it. Shadowing lets you reuse a variable name by creating a brand-new binding with the let keyword. This effectively "shadows" the old variable, making it inaccessible in favor of the new one.

Example: Data Transformation

fn main() {
    let input = "   42   ";
    
    // Shadowing allows us to change the type from &str to usize
    let input = input.trim();
    let input: usize = input.parse().expect("Not a number!");

    println!("The parsed number is: {}", input); // 42
}

Why shadowing is powerful:

  • It lets you change the data type while keeping a descriptive name.
  • The variable remains immutable after the final transformation. This prevents accidental changes later in your function.

Solution 3: Using Expressions for Assignment

A common trap for beginners is trying to initialize a variable inside an if/else block. Because if blocks in Rust are expressions, you don't need to declare a variable and then assign to it. You can simply return the value from the block directly.

The Wrong Way:

let status_code;
if success {
    status_code = 200;
} else {
    status_code = 404;
}
// If you try to change status_code here, you'll hit E0384

The Rust Way:

let is_authenticated = true;

// Bind the result of the entire 'if' expression to the variable
let status_code = if is_authenticated {
    200
} else {
    401
};

println!("Response: {}", status_code);

This pattern ensures status_code is only assigned once. It satisfies the compiler's immutability rules while keeping your code clean and functional.

Verification and Testing

Once you apply a fix, verify it with these steps:

  • Run cargo check in your terminal. This validates your syntax and types without the overhead of a full build, saving you 5–10 seconds on larger projects.
  • If the check passes, execute cargo run to confirm your logic works as intended.
  • Watch for "unused mut" warnings. If Rust tells you a variable doesn't need to be mutable, remove the mut keyword to keep your code safe.

Best Practices

To keep E0384 errors at bay, try these habits:

  • Default to Immutability: Start every variable with let. Only add mut when the compiler or your logic specifically demands it.
  • Leverage Match and If: Use match or if expressions to calculate values. This is almost always cleaner than declaring a variable and updating it inside branches.
  • Keep Scopes Small: If a variable must be mutable, wrap that logic in a small block {} or a helper function. This limits the area where the value can change, making debugging much easier.

Related Error Notes