Rust is like that overprotective friend who won’t let you borrow their car without a full background check. It’s all about safety—especially when it comes to control flow and ownership. But don’t worry! This Monday with Rust over Polkadot its time to learn all of it with some fun and increasingly complex code examples. Buckle up!
Rust’s if
statements work like they do in most languages, but with a little twist—no parentheses required!
fn main() {
let temperature = 30;
if temperature > 25 {
println!("It's hot! Time for some ice cream! ");
} else {
println!(" it's manageable.");
}
}
Now, let’s spice things up with a match
statement, Rust’s supercharged version of switch
.
fn main() {
let weather = "rainy";
match weather {
"sunny" => println!("Break out the sunglasses! "),
"rainy" => println!("Better bring an umbrella! "),
"snowy" => println!("Time to build a snowman! "),
_ => println!("Hmm... I have no idea what to do."),
}
}
With match
, Rust makes sure you cover all cases. No sneaky bugs here! 🐛
Rust has three main loops: for
, while
, and loop
. Let’s level up our complexity.
for
)fn main() {
let snacks = ["apple", "banana", "chocolate"];
for snack in snacks.iter() {
println!("Eating: {}", snack);
}
}
while
)fn main() {
let mut fuel = 3;
while fuel > 0 {
println!("Vroom! Fuel left: {}", fuel);
fuel -= 1;
}
println!("Out of fuel! Time to refuel. ");
}
loop
)fn main() {
let mut counter = 0;
loop {
println!("Counting: {}", counter);
counter += 1;
if counter == 5 {
println!("Stopping at 5! ");
break;
}
}
}
Rust has strict ownership rules, making sure memory safety is a top priority.
fn main() {
let snack = String::from(" Donut");
let snack2 = snack; // Ownership moves here!
// println!("First snack: {}", snack); // ❌ ERROR! `snack` is gone!
println!("Second snack: {}", snack2); // ✅ Works!
}
Rust doesn’t allow snack
to be used after it’s moved—just like a jealous toddler who won’t share their toys. 🧸
Rust lets you borrow a variable instead of moving it, but you must follow strict borrowing rules!
fn main() {
let snack = String::from(" Cookie");
eat_snack(&snack); // Borrowing instead of moving
println!("Still have my snack: {}", snack); // ✅ Works fine!
}
fn eat_snack(snack: &String) {
println!("Eating: {}", snack);
}
📝 Rule: You can have one mutable reference OR multiple immutable references, but not both at the same time.
fn main() {
let mut drink = String::from("Soda");
let drink_ref1 = &drink;
let drink_ref2 = &drink;
// let drink_mut = &mut drink; // ❌ ERROR! Cannot mix mutable and immutable references.
println!("{} and {}", drink_ref1, drink_ref2); // ✅ Works fine!
}
Rust ensures references live long enough to be useful. Here’s an example with an explicit lifetime annotation:
fn main() {
let snack = String::from("Pizza");
let result;
{
let topping = String::from("Cheese");
result = best_combo(&snack, &topping);
println!("{}", result);
} // `topping` goes out of scope here
// println!("Still have: {}", result); // ❌ ERROR! `topping` is gone!
}
fn best_combo<'a>(food: &'a String, topping: &'a String) -> String {
format!("Best combo: {} + {}", food, topping)
}
Rust makes sure result
doesn’t outlive the topping
. If it did, we'd have a dangling reference (aka ghost memory! 👻).
Mastering control flow and ownership is like learning how to drive a Formula 1 car—exciting but with strict safety rules. 💨
Use if/else/match to make decisions.
Use for/while/loop to repeat tasks.
Understand ownership to avoid Rust's strict errors.
Borrow responsibly to keep your variables alive.
Use lifetimes to ensure references don’t go out of scope.
Congrats, Rustacean! 🦀 Keep coding, keep laughing, and may the rust be with you! 🚀