[Rust] Variables and Mutability: Immutability, mut, Constants, Shadowing
These are my notes on The Rust Programming Language, chapter 3.1 "Variables and Mutability"; the examples are based on that chapter.
This post covers variables and mutability, constants, and shadowing.
Variables are Immutable by Default
In Rust, when you declare a variable using let, it is immutable by default. This means once you bind a value to a name, you can't change it.
If one part of your code assumes a value will stay the same but another part changes it, you can end up with a bug. Rust prevents this at compile time.
Take a look at this code that won't compile:
fn main() {
let x = 5;
println!("The value of x is: {x}");
x = 6; // Error! Cannot assign twice to immutable variable
println!("The value of x is: {x}");
}
Compiling it fails with error[E0384]: cannot assign twice to immutable variable x``.
Making Variables Mutable with mut
Of course, sometimes you need to change a value. In those cases, you can opt-out of the default behavior by adding the mut keyword in front of the variable name.
fn main() {
let mut x = 5;
println!("The value of x is: {x}");
x = 6; // This works perfectly!
println!("The value of x is: {x}");
}
Using mut also tells anyone reading your code that this variable is meant to change.
Constants
Variables (even immutable ones) are different from constants. Constants are declared using the const keyword and are always immutable.
Here are the rules for constants:
- You can't use
mutwith them. - You must annotate the type (like
u32orf64). - They can only be set to values that can be calculated at compile time.
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
Constants are useful for values that never change while the program runs, like the speed of light or a maximum health limit in a game.
Shadowing
Rust supports shadowing. This is when you declare a new variable with the same name as a previous one. The second variable "shadows" the first one.
fn main() {
let x = 5;
let x = x + 1; // x is now 6
{
let x = x * 2; // x is now 12 inside this scope
println!("The value of x in the inner scope is: {x}");
}
println!("The value of x is: {x}"); // x is back to 6 here
}
Why use Shadowing instead of mut?
- Change Types: You can change the data type while keeping the same name (e.g., converting a string of spaces
" "to its length3). - Keep Immutability: After the transformation is done, the variable remains immutable, keeping your code safe.
Summary Table
| Feature | Immutable Variable | Mutable Variable (mut) | Constant (const) |
|---|---|---|---|
| Changeable? | No | Yes | No |
| Type Annotation | Optional | Optional | Required |
| Shadowing? | Yes | Yes | No |
| Scope | Local | Local | Global or Local |