Understanding Variables and Mutability in Rust: A Beginner's Guide
Rust's approach to variables is unique compared to many other programming languages, especially if you're coming from languages like JavaScript, Python, or Java. This guide will walk you through how variables work in Rust, with a focus on mutability, constants, and shadowing. Getting Started Before diving in, make sure you have Rust installed on your system. You can check by running: rustc --version If you need to install Rust, visit rust-lang.org for instructions. Variables in Rust: Immutable by Default One of Rust's core principles is that variables are immutable by default. This means once you assign a value to a variable, you cannot change it. Step 1: Create a new Rust project cargo new rust_variables cd rust_variables Step 2: Edit the main.rs file Open src/main.rs in your editor and replace its contents with: fn main() { let x = 5; println!("The value of x is: {x}"); // This will cause a compilation error // x = 6; // println!("The value of x is: {x}"); } Step 3: Run your program cargo run You should see the output: The value of x is: 5 If you uncomment the lines that try to reassign x, you'll get a compiler error: error[E0384]: cannot assign twice to immutable variable `x` Making Variables Mutable When you need to change a variable's value, you must explicitly mark it as mutable using the mut keyword. Step 1: Update your code to use mut fn main() { let mut x = 5; println!("The value of x is: {x}"); x = 6; // Now this works! println!("The value of x is: {x}"); } Step 2: Run your program again cargo run Output: The value of x is: 5 The value of x is: 6 Working with Constants Constants are similar to immutable variables but with a few key differences: They're declared using the const keyword The type must be explicitly annotated They can be declared in any scope, including global scope They can only be set to a constant expression, not something computed at runtime Step 1: Add a constant to your program // Global constant const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3; fn main() { let mut x = 5; println!("The value of x is: {x}"); x = 6; println!("The value of x is: {x}"); println!("Three hours in seconds: {THREE_HOURS_IN_SECONDS}"); } Step 2: Run your program cargo run Output: The value of x is: 5 The value of x is: 6 Three hours in seconds: 10800 Using Variable Shadowing Rust allows you to declare a new variable with the same name as a previous one, which "shadows" the previous variable. Step 1: Update your code to use shadowing fn main() { let x = 5; println!("Initial value of x: {x}"); let x = x + 1; // Shadows the previous x println!("After first shadowing: {x}"); { let x = x * 2; // Shadows x in this scope only println!("Value of x in inner scope: {x}"); } println!("Value of x in outer scope: {x}"); } Step 2: Run your program cargo run Output: Initial value of x: 5 After first shadowing: 6 Value of x in inner scope: 12 Value of x in outer scope: 6 Shadowing vs. Mutability While both shadowing and mutability allow you to change values, they have key differences: Type Changes: Shadowing lets you change the type of a variable while reusing the same name fn main() { let spaces = " "; // This is a string println!("spaces is a string: '{spaces}'"); let spaces = spaces.len(); // Now it's a number println!("spaces is now a number: {spaces}"); } Safety: Shadowing requires the let keyword, making accidental reassignments less likely Best Practices Favor Immutability: Use immutable variables by default and only add mut when necessary Use Constants for Values That Don't Change: If a value should never change and is used in multiple places, make it a constant Use Shadowing for Transformations: When you need to transform a value and then keep it immutable, shadowing is a good option Choose Clear Names: Use descriptive variable names that indicate the purpose of the value Troubleshooting Common Errors Error: Cannot assign twice to immutable variable error[E0384]: cannot assign twice to immutable variable `x` Solution: Add mut to the variable declaration: let mut x = 5; Error: Mismatched types error[E0308]: mismatched types expected `&str`, found `usize` Solution: Use shadowing instead of mutation when changing types: let spaces = " "; let spaces = spaces.len(); // Use shadowing Conclusion Understanding Rust's approach to variables and mutability is fundamental to writing good Rust code. The language's design encourages you to think carefully about which values in

Rust's approach to variables is unique compared to many other programming languages, especially if you're coming from languages like JavaScript, Python, or Java. This guide will walk you through how variables work in Rust, with a focus on mutability, constants, and shadowing.
Getting Started
Before diving in, make sure you have Rust installed on your system. You can check by running:
rustc --version
If you need to install Rust, visit rust-lang.org for instructions.
Variables in Rust: Immutable by Default
One of Rust's core principles is that variables are immutable by default. This means once you assign a value to a variable, you cannot change it.
Step 1: Create a new Rust project
cargo new rust_variables
cd rust_variables
Step 2: Edit the main.rs file
Open src/main.rs
in your editor and replace its contents with:
fn main() {
let x = 5;
println!("The value of x is: {x}");
// This will cause a compilation error
// x = 6;
// println!("The value of x is: {x}");
}
Step 3: Run your program
cargo run
You should see the output:
The value of x is: 5
If you uncomment the lines that try to reassign x
, you'll get a compiler error:
error[E0384]: cannot assign twice to immutable variable `x`
Making Variables Mutable
When you need to change a variable's value, you must explicitly mark it as mutable using the mut
keyword.
Step 1: Update your code to use mut
fn main() {
let mut x = 5;
println!("The value of x is: {x}");
x = 6; // Now this works!
println!("The value of x is: {x}");
}
Step 2: Run your program again
cargo run
Output:
The value of x is: 5
The value of x is: 6
Working with Constants
Constants are similar to immutable variables but with a few key differences:
- They're declared using the
const
keyword - The type must be explicitly annotated
- They can be declared in any scope, including global scope
- They can only be set to a constant expression, not something computed at runtime
Step 1: Add a constant to your program
// Global constant
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
fn main() {
let mut x = 5;
println!("The value of x is: {x}");
x = 6;
println!("The value of x is: {x}");
println!("Three hours in seconds: {THREE_HOURS_IN_SECONDS}");
}
Step 2: Run your program
cargo run
Output:
The value of x is: 5
The value of x is: 6
Three hours in seconds: 10800
Using Variable Shadowing
Rust allows you to declare a new variable with the same name as a previous one, which "shadows" the previous variable.
Step 1: Update your code to use shadowing
fn main() {
let x = 5;
println!("Initial value of x: {x}");
let x = x + 1; // Shadows the previous x
println!("After first shadowing: {x}");
{
let x = x * 2; // Shadows x in this scope only
println!("Value of x in inner scope: {x}");
}
println!("Value of x in outer scope: {x}");
}
Step 2: Run your program
cargo run
Output:
Initial value of x: 5
After first shadowing: 6
Value of x in inner scope: 12
Value of x in outer scope: 6
Shadowing vs. Mutability
While both shadowing and mutability allow you to change values, they have key differences:
- Type Changes: Shadowing lets you change the type of a variable while reusing the same name
fn main() {
let spaces = " "; // This is a string
println!("spaces is a string: '{spaces}'");
let spaces = spaces.len(); // Now it's a number
println!("spaces is now a number: {spaces}");
}
-
Safety: Shadowing requires the
let
keyword, making accidental reassignments less likely
Best Practices
-
Favor Immutability: Use immutable variables by default and only add
mut
when necessary - Use Constants for Values That Don't Change: If a value should never change and is used in multiple places, make it a constant
- Use Shadowing for Transformations: When you need to transform a value and then keep it immutable, shadowing is a good option
- Choose Clear Names: Use descriptive variable names that indicate the purpose of the value
Troubleshooting Common Errors
Error: Cannot assign twice to immutable variable
error[E0384]: cannot assign twice to immutable variable `x`
Solution: Add mut
to the variable declaration:
let mut x = 5;
Error: Mismatched types
error[E0308]: mismatched types
expected `&str`, found `usize`
Solution: Use shadowing instead of mutation when changing types:
let spaces = " ";
let spaces = spaces.len(); // Use shadowing
Conclusion
Understanding Rust's approach to variables and mutability is fundamental to writing good Rust code. The language's design encourages you to think carefully about which values in your program can change and which ones should remain constant, leading to more maintainable and less error-prone code.
Remember these key points:
- Variables are immutable by default
- Use
mut
when you need to change a variable - Constants use the
const
keyword and must include a type annotation - Shadowing allows you to reuse variable names and change types
With these concepts in mind, you're well on your way to writing more Rustic code.