Rust
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Ownership and Borrowing
-
Section 4: Structuring Data
-
Section 5: Collections and Error Handling
-
Section 6: Traits and Generics
-
Section 7: Concurrency
-
Section 8: Building for the Web
-
Section 9: Memory and Performance
-
Section 10: More Standard Library and Ecosystem
-
Section 11: Advanced Rust
-
Section 12: Rust for Systems and WebAssembly
-
Section 13: Tooling and Best Practices
-
Section 14: Data Structures and Algorithms in Rust
-
Section 15: Practical Projects
-
Section 16: Interview Practice
-
Section 17: std::collections In Depth
-
Section 18: std::io and std::fs In Depth
-
Section 19: std::net
-
Section 20: std::option and std::result In Depth
-
Section 21: std::iter In Depth
-
Section 22: std::sync In Depth
-
Section 23: std::string and std::str
-
Section 24: Cargo and Crates.io Ecosystem
-
Section 25: Popular Crates Ecosystem
-
Section 26: Rust Memory Model Deep Dive
-
Section 27: More Practice Exercises
-
Section 28: More Interview Practice
-
Section 29: Async Rust Deep Dive
-
Section 30: Tokio Ecosystem In Depth
-
Section 31: Error Handling Ecosystem Deep Dive
-
Section 32: Serde In Depth
-
Section 33: Web Development with Rust Deep Dive
-
Section 34: Database Access Ecosystem
-
Section 35: Rust for Embedded Systems Deep Dive
-
Section 36: Rust Macros In Depth
-
Section 37: Advanced Trait System
-
Section 38: Unsafe Rust In Depth
-
Section 39: Rust CLI Development
-
Section 40: Testing Ecosystem Deep Dive
-
Section 41: WebAssembly Deep Dive
-
Section 42: Rust Design Patterns
-
Section 43: More Data Structures in Rust
-
Section 44: Final Practice Projects
-
Section 45: Rust Performance Optimization
-
Section 46: Rust Ecosystem Tooling
-
Section 47: More Interview and Review
6: Variables and Mutability
Imagine you're handling a legal contract. Once that contract is signed and notarized, you can't just take an eraser to the "Payment Amount" line and scribble in a new number. If you want a different amount, you have to draft an entirely new contract. Now, compare that to a whiteboard in a conference room. You write a number up there, and five minutes later, you can wipe it away and write something else in the exact same spot.
In Rust, variables work exactly like this. By default, every variable you create is like that signed contract: immutable. If you want the whiteboard experience, you have to explicitly ask for it.
The Default: Ink on Paper
When you use let, you're telling Rust, "This value is set, and it isn't going to change." I'll be honest—when I first started with Rust, this drove me crazy. I kept trying to update variables and the compiler kept yelling at me. But there's a method to the madness. By making things immutable by default, Rust eliminates a whole category of bugs where a value changes unexpectedly in a complex program.
fn main() {
let player_name = "Aris";
println!("Welcome, {}!", player_name);
// This line would cause a compiler error:
// player_name = "Zelda";
}
If you tried to run that last line, Rust would stop you immediately. It's not being stubborn; it's protecting you. It ensures that once player_name is set, it stays that way for the rest of its life.
The Escape Hatch: Using mut
Obviously, you can't build a real app if nothing ever changes. You need a way to track things like a player's health, a shopping cart total, or a countdown timer. This is where the mut keyword comes in. Adding mut transforms your "signed contract" into a "whiteboard."
fn main() {
let mut health = 100;
println!("Starting health: {}", health);
health = 80; // This is perfectly legal now
println!("After taking damage: {}", health);
health = 60;
println!("Ouch, again: {}", health);
}
I usually recommend staying immutable as long as possible. Only add mut when you absolutely have to. It makes your code much easier to reason about because you can look at a variable and know for a fact it isn't changing behind your back.
Starting Fresh with Shadowing
There is a third option that often confuses people: shadowing. This happens when you use the let keyword again with the same variable name. This isn't the same as mutability. You aren't changing the value inside the "bucket"; you're throwing the old bucket away and putting a brand new one in its place with the same name.
Shadowing is incredibly useful when you want to change the type of a variable but keep the name. For example, imagine taking a string of numbers from a user and needing to turn it into an actual integer.
fn main() {
let space_fuel = "42"; // This is a string (&str)
println!("Fuel string: {}", space_fuel);
// We "shadow" the previous variable.
// It's now an integer (i32), but keeps the same name.
let space_fuel: i32 = space_fuel.parse().expect("Not a number!");
println!("Fuel integer: {}", space_fuel);
}
If we had used mut for this, it would have failed because a mut variable cannot change its data type. Shadowing lets us transform the data while keeping our naming clean.
📋 Practical Task
Build a Spaceship Fuel Monitor
Write a small Rust program that simulates a spaceship's fuel system. Your code should demonstrate all three concepts we covered: immutability, mutability, and shadowing.
- Part 1: Create an immutable variable called
ship_nameand assign it a value. - Part 2: Create a mutable variable called
fuel_levelstarting at 100. Simulate the ship taking off by decreasing this value twice (e.g., subtracting 20, then subtracting 10). - Part 3: Create a variable called
fuel_reportas a string (e.g.,"50"). Then, use shadowing to convert thatfuel_reportstring into an actual integer so it can be used for calculations. - Part 4: Print all three variables to the console to verify they updated correctly.
There are no comments for now.