Cover photo

Mastering Control Flow & Ownership in Rust

Fabian Owuor

Fabian Owuor

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!


1. Control Flow & Functions: Rust’s Decision-Making Powers

Conditional Statements: If, Else, and Match

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! 🐛


Loops: for, while, and loop (Oh My!)

Rust has three main loops: for, while, and loop. Let’s level up our complexity.

The "Polite" Loop (for)

fn main() {
    let snacks = ["apple", "banana", "chocolate"];
    
    for snack in snacks.iter() {
        println!("Eating: {}", snack);
    }
}

The "Cautious" Loop (while)

fn main() {
    let mut fuel = 3;
    
    while fuel > 0 {
        println!("Vroom! Fuel left: {}", fuel);
        fuel -= 1;
    }
    println!("Out of fuel! Time to refuel. ");
}

The "Wild" Loop (loop)

fn main() {
    let mut counter = 0;
    
    loop {
        println!("Counting: {}", counter);
        counter += 1;
        
        if counter == 5 {
            println!("Stopping at 5! ");
            break;
        }
    }
}

2. Ownership, Borrowing, and Lifetimes: Rust’s VIP Security System

Ownership: Who Owns This? 🤔

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. 🧸


Borrowing: Sharing is Caring (With Rules!)

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!
}

Lifetimes: Preventing Dangling References

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! 👻).


Conclusion: You’re Now a Rust Padowan!

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! 🚀

Mastering Control Flow & Ownership in Rust