Assignment Operators
The assignment operator (=) stores a new value into an existing mutable storage location (called a place). Unlike let x = value;—which declares a brand-new variable binding—an assignment expression x = value; overwrites the contents of an already-declared variable, field, array element, or dereferenced pointer.
Declaration (let) vs Reassignment (=)
| Syntax | Construct | Creates New Binding? | Requires mut on Existing Variable? |
Can Change Variable's Type? |
|---|---|---|---|---|
let x = 10; |
let declaration statement |
Yes (or shadows existing x) |
No | Yes (when shadowing) |
x = 20; |
Assignment expression | No (mutates existing storage slot) | Yes | No (type is fixed at declaration) |
fn main() {
let mut temperature = 20; // Declaration with `let mut`
println!("Initial: {temperature}");
temperature = 25; // Reassignment with `=`
println!("Updated: {temperature}");
}
Initial: 20
Updated: 25
Place Expressions (Left-Hand Side) vs Value Expressions (Right-Hand Side)
In target = source, the right-hand side (source) can be any expression that produces a value of the matching type, while the left-hand side (target) must be a mutable place expression (also known as an lvalue) that refers to a concrete memory location:
Valid Mutable Place (Left-Hand Side of =) |
Example | What Is Overwritten |
|---|---|---|
| Mutable local variable | count = 5; |
The stack slot of count |
| Mutable struct field | point.x = 10.0; |
The x field inside point |
| Mutable tuple field | pair.0 = "updated"; |
The first element inside pair |
Mutable array or Vec element |
scores[2] = 99; |
Element at index 2 |
| Dereferenced mutable reference | *counter_ref = 42; |
The target memory pointed to by counter_ref: &mut i32 |
| Destructuring pattern of places | (a, b) = (b, a); |
Multiple mutable places simultaneously |
struct Point {
x: i32,
y: i32,
}
fn reset_to_zero(slot: &mut i32) {
*slot = 0; // Assigns through a dereferenced mutable reference
}
fn main() {
let mut pt = Point { x: 3, y: 7 };
let mut items = [10, 20, 30];
pt.x = 15; // Struct field assignment
items[1] = 200; // Array element assignment
reset_to_zero(&mut pt.y);
println!("Point: ({}, {}), items: {items:?}", pt.x, pt.y);
}
Point: (15, 0), items: [10, 200, 30]
Type of an Assignment Expression (())
In C, C++, Java, Python, and JavaScript, an assignment x = 5 evaluates to the assigned value (5). That design allows chained assignments (a = b = 5), but it also enables a notorious class of bugs where a typo if (x = 5) silently overwrites x and tests whether 5 is non-zero.
In Rust, every assignment expression place = value evaluates to the unit value (), not the assigned value!
C / C++: Rust:
(x = 5) ---> evaluates to 5 (x = 5) ---> evaluates to ()
fn main() {
let mut x = 0;
// The assignment `x = 42` is an expression that produces `()`
let assignment_result: () = (x = 42);
println!("x = {x}, assignment_result = {assignment_result:?}");
}
x = 42, assignment_result = ()
Because x = 5 evaluates to () (and if requires a bool), accidentally writing = instead of == inside an if or while condition is caught immediately at compile time:
// ❌ does not compile
fn main() {
let mut status = 1;
if status = 2 {
println!("Matched");
}
}
error[E0308]: mismatched types
--> src/main.rs:4:8
|
4 | if status = 2 {
| ^^^^^^^^^^ expected `bool`, found `()`
|
help: you might have meant to compare for equality
|
4 | if status == 2 {
| +
error: aborting due to 1 previous error
Why a = b = 0 Does Not Compile as Expected
Because b = 0 evaluates to (), writing a = b = 0; is parsed as a = (b = 0);, which tries to assign () into a! Instead, write two separate statements or use a destructuring assignment (a, b) = (0, 0);.
Destructuring Assignment
You can assign to multiple existing mutable variables simultaneously using a tuple, array, or struct pattern on the left-hand side of = without using let:
fn main() {
let mut a = 10;
let mut b = 99;
// Swap `a` and `b` in one statement without a temporary variable
(a, b) = (b, a);
println!("After swap: a = {a}, b = {b}");
// Assign first and last elements from an array, ignoring the middle with `..`
let mut first = 0;
let mut last = 0;
[first, .., last] = [100, 200, 300, 400];
println!("first = {first}, last = {last}");
}
After swap: a = 99, b = 10
first = 100, last = 400
In (a, b) = (b, a);, Rust evaluates the right-hand tuple (b, a) completely before writing the values into a and b.
Memory Behavior: Dropping the Old Value on Reassignment
When you reassign a variable whose type manages resources or heap memory (implements Drop, such as String or Vec<T>), Rust automatically drops the old value stored in that variable before moving the new value into its place:
struct Buffer(&'static str);
impl Drop for Buffer {
fn drop(&mut self) {
println!(" [Drop] Freed {}", self.0);
}
}
fn main() {
let mut active = Buffer("first buffer");
println!("About to reassign `active`...");
// 1. `Buffer("second buffer")` is created
// 2. Old value `Buffer("first buffer")` is dropped immediately!
// 3. `active` now holds `Buffer("second buffer")`
active = Buffer("second buffer");
println!("After reassignment, active holds: {}", active.0);
}
About to reassign `active`...
[Drop] Freed first buffer
After reassignment, active holds: second buffer
[Drop] Freed second buffer
Notice that first buffer is freed immediately on the line active = Buffer("second buffer");, whereas second buffer lives until main exits.
Common Errors
Assigning to an Immutable Binding (E0384)
Every variable or container on the left-hand side of = must be declared with mut:
// ❌ does not compile
fn main() {
let x = 10;
x = 20;
}
error[E0384]: cannot assign twice to immutable variable `x`
--> src/main.rs:3:5
|
2 | let x = 10;
| -
| |
| first assignment to `x`
| help: consider making this binding mutable: `mut x`
3 | x = 20;
| ^^^^^^ cannot assign twice to immutable variable
error: aborting due to 1 previous error
Invalid Left-Hand Side of Assignment (E0070)
Expressions that compute a temporary value (such as arithmetic x + 1 or a function call get_value() that returns a value rather than &mut T) are value expressions, not places, and cannot appear on the left-hand side of =:
// ❌ does not compile
fn main() {
let mut x = 5;
x + 1 = 10;
}
error[E0070]: invalid left-hand side of assignment
--> src/main.rs:3:11
|
3 | x + 1 = 10;
| ----- ^
| |
| cannot assign to this expression
error: aborting due to 1 previous error