---
title: "1.4 Variables, mutability, and initialization"
description: "Why Rust variables are immutable by default, what let mut does, and how the compiler makes uninitialized reads impossible."
url: "https://learnrust.net/chapter-1/variables-mutability-initialization/"
last_updated: "2026-06-11"
---

# 1.4 Variables, mutability, and initialization

In the previous lesson ([1.3](https://learnrust.net/chapter-1/values-and-variables/)), we created variables and gave them values. This lesson is about changing those values, and it's the first place Rust's personality really shows.

## Assignment

Giving a variable a value at the moment it's created is called **initialization** (`let width = 5;`). Giving an existing variable a new value later is called **assignment**, and it uses `=`, the **assignment operator**:

```rust
fn main() {
    let mut width = 5;
    println!("width is {width}");
    width = 7;
    println!("width is now {width}");
}
```

```
width is 5
width is now 7
```

Assignment replaces the old value; the 5 is gone, and `width`'s box in memory holds a 7. So far this matches every language you've ever heard of, except for one small word doing big work on the first line: `mut`.

## Variables are immutable by default

Delete the `mut` and watch what happens:

```rust
fn main() {
    let width = 5;
    println!("width is {width}");
    width = 7;
    println!("width is now {width}");
}
```

```
error[E0384]: cannot assign twice to immutable variable `width`
 --> src/main.rs:4:5
  |
2 |     let width = 5;
  |         ----- first assignment to `width`
4 |     width = 7;
  |     ^^^^^^^^^ cannot assign twice to immutable variable
  |
help: consider making this binding mutable
  |
2 |     let mut width = 5;
  |         +++
```

In Rust, a plain `let` variable is **immutable**: once initialized, its value cannot be changed. If you want a variable you can assign to, you must say so up front with `let mut` (`mut` as in **mutable**, changeable). The error above is the compiler holding you to that, and notice it's a good error: it shows where the variable was created, where the rule broke, and the exact edit that fixes it.

New programmers usually meet this rule with polite disbelief. A variable that can't vary? But the default earns its keep twice over.

> **Key insight**
>
> First: most variables never need to change. Look at real programs and you'll find most values are computed once, used, and never touched again; "changes later" is the minority case. Rust makes the common case the default and the special case the marked one.
>
> Second, and bigger: `mut` is information. When you read `let mut attempts`, the code is telling you "this value will change, watch it." When you read `let price`, you can stop tracking it; the compiler guarantees it's the same value everywhere. In a 50-line program this is a nicety. In a 50,000-line program it's a searchlight.

Accidentally assigning to something that shouldn't change is also a classic source of bugs, and in Rust that whole category dies at compile time. You'll notice this is the ownership chapter's pitch in miniature: say what you intend, and the compiler enforces it.

> **Warning**
>
> `=` is assignment. `==` (two equals signs) is the *comparison* operator, "are these equal?", which you'll meet properly in chapter 4. Mixing them up is a rite of passage in most languages, where `if (x = 5)` silently assigns instead of comparing. File this warning away; when you reach lesson 4.7 you'll learn why Rust turns that classic bug into a compile error too.

## Initialization is enforced

Here's the second rule of this lesson, and it kills an entire genre of bug. You can declare a variable without a value:

```rust
let x: i32;
```

(Note we have to state the type now; with no value, there's nothing to infer from.) But try to *use* it before it has one:

```rust
fn main() {
    let x: i32;
    println!("x is {x}");
}
```

```
error[E0381]: used binding `x` isn't initialized
 --> src/main.rs:3:21
  |
2 |     let x: i32;
  |         - binding declared here but left uninitialized
3 |     println!("x is {x}");
  |                     ^ `x` used here but it isn't initialized
  |
help: consider assigning a value
  |
2 |     let x: i32 = 42;
  |                ++++
```

The rule is precise: Rust doesn't demand a value at the declaration, it demands one before the first *use*. This compiles fine:

```rust
fn main() {
    let x: i32;
    x = 10;
    println!("x is {x}");
}
```

```
x is 10
```

(A fine point you can file away: that `x = 10` doesn't need `mut`, because it's the variable's *first* value. It's initialization arriving late, not re-assignment.)

> **Key insight**
>
> What would happen without this rule? In C++, reading an uninitialized variable is legal to write and gives you whatever garbage bits happened to be in that memory box: a different "value" on different runs, on different machines, sometimes even masked in debug builds. learncpp.com needs a whole lesson on the undefined behavior this unleashes. This course covers the same hazard with one sentence: in Rust, the program that reads uninitialized memory doesn't compile. An entire lesson's worth of horror, refunded.

## Quiz time

**Question #1**

What's the difference between initialization and assignment?

<details class="solution">
<summary>Show solution</summary>

Initialization gives a variable its first value. Assignment replaces the value of a variable that already has one, and only `mut` variables allow it.

</details>

**Question #2**

This is the course's first *predict the compiler error* question, a genre you'll see often: the program below does not compile. What will the compiler complain about, and what's the fix?

```rust
fn main() {
    let count = 10;
    count = count + 1;
    println!("{count}");
}
```

<details class="solution">
<summary>Show solution</summary>

`count` is immutable (no `mut`), and line 3 assigns to it. The compiler reports error E0384, "cannot assign twice to immutable variable `count`", points at both the original `let` and the offending assignment, and suggests the fix: `let mut count = 10;`.

</details>

**Question #3**

What does this program print?

```rust
fn main() {
    let mut x = 1;
    println!("{x}");
    x = 2;
    x = 3;
    println!("{x}");
}
```

<details class="solution">
<summary>Show solution</summary>

```
1
3
```

The assignments to 2 and then 3 each replace the previous value, and only the final value is printed. (The compiler does flag the `2` with an unused-assignment warning, since nothing reads it before it's overwritten. It compiles and runs anyway.)

</details>

**Question #4**

Which of these variables needs `mut`?

```rust
let player_name = "Ada";
let high_score = 0;
// ... later in the program:
// high_score = 250;
```

<details class="solution">
<summary>Show solution</summary>

Only `high_score`, because it's assigned again later. `player_name` is never changed, so the immutable default is exactly right for it. Declaring it `mut` anyway wouldn't be an error (just a compiler warning about an unneeded `mut`), but it would tell readers a lie about your intentions.

</details>

So variables can change, with permission. Printing them flexibly is next: `println!` has more tricks than the `{x}` you've seen so far.

## Sitemap

See the full [sitemap](https://learnrust.net/sitemap.md) for all pages.
