[Rust] Variables and Mutability: How Rust Keeps Your Data Safe
Welcome to the world of Rust! As you start your journey, you'll notice that Rust has some unique ways of handling data. Today, we're going to dive into variables, mutability, and how Rust helps you write rock-solid code. I'll be guiding you through these concepts with some examples and questions along the way.
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.
Think of it as a safety feature. If one part of your code assumes a value will stay the same, but another part changes it, you might end up with a nasty 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}");
}
If you try to run this, the Rust compiler will step in and say, "Hey, you said x wouldn't change, but you're trying to change it!"
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 is great because it tells anyone reading your code (including your future self!) that this specific variable is intended to change.
Constants: The Forever Values
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 perfect for values that never change throughout the entire life of the program, like the speed of light or a maximum health limit in a game.
The Magic of Shadowing
Rust allows a cool trick called 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 |