Using Variables in Rust
Variables are one of the first Rust concepts to learn because Rust makes mutability, types, and ownership explicit. This guide covers variable declarations, mutable values, type annotations, constants, and shadowing.
What Is a Variable in Rust?
A variable binds a name to a value. Rust is statically typed, so the compiler knows the type of every variable at compile time and checks that operations use compatible types.
Declare Variables
Use let:
fn main() {
let x = 5;
println!("The value of x is: {}", x);
}Variables are immutable by default. After x is initialized, you cannot assign a different value to the same binding unless it was declared mutable.
Mutability
Use mut when a binding needs to change:
fn main() {
let mut y = 10;
println!("Initial value of y: {}", y);
y = 20;
println!("New value of y: {}", y);
}Default immutability makes mutation intentional and easier to reason about.
Type Annotations and Inference
Rust often infers a type from context, but you can write it explicitly:
fn main() {
let z: i32 = 15;
println!("The value of z is: {}", z);
}Common built-in types include:
- Signed integers:
i8,i16,i32,i64,i128,isize - Unsigned integers:
u8,u16,u32,u64,u128,usize - Floating point:
f32,f64 - Boolean:
bool - Unicode scalar value:
char - String data:
&strandString
Constants vs. Variables
Constants use const, require an explicit type, and must be initialized with a constant expression:
const MAX_POINTS: u32 = 100_000;
fn main() {
println!("Maximum points: {}", MAX_POINTS);
}Constants are useful for values that conceptually belong to the program or module rather than to a runtime binding.
Shadowing
Rust lets you declare a new variable with the same name as an earlier binding:
fn main() {
let x = 5;
let x = x + 1;
let x = x * 2;
println!("The value of x is: {}", x);
}Shadowing creates a new binding; it is different from mutation. Because the new binding can have a different type, shadowing is useful for transformations such as parsing:
let spaces = " ";
let spaces = spaces.len();Best Practices
- Prefer immutable bindings unless mutation is part of the intended behavior.
- Use descriptive names that express the meaning of a value.
- Add explicit type annotations when inference is ambiguous or when they improve readability.
- Use shadowing when it represents a clear transformation, not merely to reuse a name indiscriminately.
- Use constants for fixed program-level values and
staticwhen a value truly needs static storage semantics.
Conclusion
Rust variables combine familiar bindings with deliberate defaults: immutable unless marked otherwise, statically typed, and integrated with the ownership system. Understanding let, mut, constants, inference, and shadowing gives you a solid base for the rest of the language.