Mutable Variables with mut
The mut keyword makes a variable binding mutable: the variable can be assigned a new value, and the value it holds can be modified in place. mut is written between let and the name.
fn main() {
let mut x = 5;
println!("x = {x}");
x = 6;
println!("x = {x}");
x += 10;
x *= 2;
println!("x = {x}");
}
x = 5
x = 6
x = 32
Syntax
let mut name: Type = value;
---
|
+-- allows reassignment and mutable borrows of this binding
mut is part of the binding, not part of the type. The type of x above is i32, not "mutable i32". The same type is used whether the binding is mutable or not.
What mut Allows
A mutable binding permits two kinds of operations that an immutable binding rejects:
| Operation | Example | Needs mut |
|---|---|---|
| Assign a new value | x = 6; |
yes |
| Compound assignment | x += 1; |
yes |
| Assign to a field, element, or tuple position | p.x = 3;, a[0] = 1;, t.0 = 2; |
yes |
Call a method that takes &mut self |
v.push(1); |
yes |
| Take a mutable reference | &mut x |
yes |
| Read the value | println!("{x}") |
no |
The next example uses mut with several kinds of values:
fn main() {
let mut numbers = vec![1, 2, 3];
numbers.push(4);
numbers[0] = 100;
numbers.retain(|n| n % 2 == 0);
println!("{numbers:?}");
let mut text = String::from("Hello");
text.push_str(", world");
text.push('!');
println!("{text}");
let mut scores = [10, 20, 30];
scores[1] += 5;
println!("{scores:?}");
let mut pair = (1, "one");
pair.0 = 2;
pair.1 = "two";
println!("{pair:?}");
}
[100, 2, 4]
Hello, world!
[10, 25, 30]
(2, "two")
Structs
Mutability applies to the whole struct binding. With mut, every field can be assigned, and fields can be modified through methods:
struct Counter {
value: u32,
label: String,
}
fn main() {
let mut counter = Counter {
value: 0,
label: String::from("clicks"),
};
counter.value += 1;
counter.label.push_str("!");
println!("{} {}", counter.label, counter.value);
}
clicks! 1
The Type Cannot Change
A mutable variable can hold a different value, but never a value of a different type. The type is fixed when the variable is declared.
// ❌ does not compile
fn main() {
let mut value = 5;
value = "five";
println!("{value}");
}
error[E0308]: mismatched types
--> main.rs:4:13
|
3 | let mut value = 5;
| - expected due to this value
4 | value = "five";
| ^^^^^^ expected integer, found `&str`
To reuse a name with a new type, use shadowing (let value = "five";), which creates a new variable.
The Unused mut Warning
The compiler warns when a variable is declared mut but is never mutated:
fn main() {
let mut count = 0;
println!("{count}");
}
warning: variable does not need to be mutable
--> main.rs:2:9
|
2 | let mut count = 0;
| ----^^^^^
| |
| help: remove this `mut`
|
= note: `#[warn(unused_mut)]` (part of `#[warn(unused)]`) on by default
The warning exists because every mut is a claim to the reader that the variable changes. A mut that does not change anything is misleading.
Copying Versus Aliasing
Assigning to a mutable variable changes only that variable. A copy made earlier is not affected:
fn main() {
let mut x = 5;
let y = x;
x = 6;
println!("{x} {y}");
let mut s1 = String::from("a");
let s2 = s1.clone();
s1.push('b');
println!("{s1} {s2}");
}
6 5
ab a
y is a copy of the integer, and s2 is an independent clone of the string. Neither one observes the later change.
Mutable Parameters and Mutable References
A function parameter is an ordinary binding, so it needs mut to be modified. A mut parameter changes only the function's own copy:
fn add_one(mut n: i32) -> i32 {
n += 1;
n
}
fn add_one_in_place(n: &mut i32) {
*n += 1;
}
fn main() {
let a = 5;
let b = add_one(a);
println!("{a} {b}");
let mut c = 5;
add_one_in_place(&mut c);
println!("{c}");
}
5 6
6
| Declaration | Meaning |
|---|---|
fn f(mut n: i32) |
The function owns a copy of n and may change that copy. The caller sees no change |
fn f(n: &mut i32) |
The function borrows the caller's variable mutably and can change it. The caller sees the change |
The caller must itself have a mut variable to lend: &mut c requires that c be declared let mut c.
mut x Versus &mut
mut on a binding and &mut in a type are different things. let mut r = &x makes the variable r reassignable, while the value it points to is still read-only. let r = &mut x makes r a fixed reference through which the pointed-to value can be changed.
fn main() {
let mut total = 10;
let r = &mut total;
*r += 5;
println!("{total}");
let first = 1;
let second = 2;
let mut pointer = &first;
println!("{pointer}");
pointer = &second;
println!("{pointer}");
}
15
1
2
| Declaration | Variable can be reassigned | Pointed-to value can be changed |
|---|---|---|
let r = &x; |
no | no |
let mut r = &x; |
yes | no |
let r = &mut x; |
no | yes |
let mut r = &mut x; |
yes | yes |
Mutable Borrowing Rules
Mutation through a reference is controlled by two rules that the compiler checks:
- At any time there can be either any number of immutable references (
&T) or exactly one mutable reference (&mut T) to a value. - References must always point to valid data.
Two mutable borrows alive at the same time are rejected:
// ❌ does not compile
fn main() {
let mut value = 1;
let a = &mut value;
let b = &mut value;
*a += 1;
*b += 1;
println!("{value}");
}
error[E0499]: cannot borrow `value` as mutable more than once at a time
--> main.rs:5:13
|
4 | let a = &mut value;
| ---------- first mutable borrow occurs here
5 | let b = &mut value;
| ^^^^^^^^^^ second mutable borrow occurs here
6 | *a += 1;
| ------- first borrow later used here
Modifying a collection while looping over it is the same kind of conflict: the loop holds an immutable borrow, so the body cannot take a mutable one.
// ❌ does not compile
fn main() {
let mut numbers = vec![1, 2, 3];
for n in &numbers {
if *n == 2 {
numbers.push(4);
}
}
}
error[E0502]: cannot borrow `numbers` as mutable because it is also borrowed as immutable
--> main.rs:6:13
|
4 | for n in &numbers {
| - --------
| | |
| | immutable borrow occurs here
| _____| immutable borrow later used here
| |
5 | | if *n == 2 {
6 | | numbers.push(4);
| | ^^^^^^^^^^^^^^^ mutable borrow occurs here
7 | | }
8 | | }
| |_____- this for loop borrows `numbers` immutably, preventing mutation within its body
|
= help: consider using an index-based loop instead, or collecting modifications into a separate collection
If push were allowed, the vector could reallocate while the loop still holds a pointer into the old buffer. Rust rejects the program instead.
Common Uses of mut
Accumulators and counters in loops:
fn main() {
let mut sum = 0;
for i in 1..=5 {
sum += i;
}
println!("{sum}");
let mut n = 27;
let mut steps = 0;
while n != 1 {
n = if n % 2 == 0 { n / 2 } else { 3 * n + 1 };
steps += 1;
}
println!("{steps}");
}
15
111
Swapping values through mutable references:
fn main() {
let mut a = 1;
let mut b = 2;
std::mem::swap(&mut a, &mut b);
println!("{a} {b}");
}
2 1
Closures that change captured state need the closure variable itself to be mut, because calling it mutates what it captured:
fn main() {
let mut counter = 0;
let mut increment = || {
counter += 1;
counter
};
increment();
increment();
println!("{counter}");
}
2
mut in Patterns
mut can be applied to individual names inside a pattern. Only the marked names are mutable:
fn main() {
let (mut a, b) = (1, 2);
a += b;
println!("{a} {b}");
}
3 2
b stays immutable. The same syntax works in function parameters and in for loops (for mut item in items).
Keeping Mutation Local
A mutable variable is only needed while the value is being built or changed. After that, the value can be rebound to an immutable name by shadowing, which prevents later accidental changes:
fn main() {
let mut values = vec![3, 1, 2];
values.sort();
let values = values;
println!("{values:?}");
}
[1, 2, 3]
let values = values; moves the vector into a new immutable binding with the same name. From that line on, the vector can only be read.
A block can confine the mutable variable entirely:
fn main() {
let values = {
let mut temp = vec![3, 1, 2];
temp.sort();
temp
};
println!("{values:?}");
}
[1, 2, 3]
Here values is immutable from the start, and temp exists only inside the block.