Why doesn't the compiler report an error when a variable not declared as mutable is modified?
What you have written is identical to:
let x: u32 = 10;
The compiler will not permit you to mutate it thereafter:
let x: u32;
x = 10;
x = 0; // Error: re-assignment of immutable variable `x`
Note that it is a compiler error if you try to use an uninitialized variable:
let x: u32;
println!("{}", x); // Error: use of possibly uninitialized variable: `x`
This feature can be pretty useful if you want to initialize the variable differently based on runtime conditions. A naive example:
let x: u32;
if condition {
x = 1;
} else if other_condition {
x = 10;
} else {
x = 100;
}
But still it will still be an error if there is a possibility that it isn't initialized:
let x: u32;
if condition {
x = 1;
} else if other_condition {
x = 10;
} // no else
println!("{:?}", x); // Error: use of possibly uninitialized variable: `x`
As mentioned, this is not mutation, but deferred initialization:
- mutation is about changing the value of an existing variable,
- deferred initialization is about declaring a variable at one point, and initializing it later.
The Rust compiler tracks whether a variable has a value at compile-time, so unlike C there is no risk of accidentally using an uninitialized variable (or unlike C++, a variable that was moved from).
The most important reason for using deferred initialization is scope.
fn main() {
let x;
let mut v = vec!();
{
x = 2;
v.push(&x);
}
println!("{:?}", v);
}
In Rust, the borrow-checker will validate that a reference cannot outlive the value it refers to, preventing dangling references.
This means that v.push(&x)
requires that x
lives longer than v
, and therefore be declared before v
.
The need for it does not crop up often, but when it does other solutions would require run-time checks.