Control Flow

Control flow allows your program to make decisions and repeat actions.

If Expressions

Basic If

fn main() {
    let number = 7;
    
    if number < 5 {
        println!("condition was true");
    } else {
        println!("condition was false");
    }
}

Python comparison:

# Python uses indentation instead of braces
number = 7

if number < 5:
    print("condition was true")
else:
    print("condition was false")

Key differences:

  • Rust: No parentheses needed around condition
  • Rust: Must use braces {}
  • Python: Uses colons : and indentation
  • Rust: Condition must be a bool (no truthy/falsy values)

Else If

fn main() {
    let number = 6;
    
    if number % 4 == 0 {
        println!("divisible by 4");
    } else if number % 3 == 0 {
        println!("divisible by 3");
    } else if number % 2 == 0 {
        println!("divisible by 2");
    } else {
        println!("not divisible by 4, 3, or 2");
    }
}

If as Expression

if is an expression in Rust and can return a value:

fn main() {
    let condition = true;
    let number = if condition { 5 } else { 6 };
    
    println!("The value of number is: {}", number);
}

Python comparison:

# Python's ternary operator
condition = True
number = 5 if condition else 6

Both branches must return the same type:

let number = if condition { 5 } else { "six" };  // ERROR: type mismatch

Loops

Rust has three kinds of loops: loop, while, and for.

The loop Keyword

Infinite loop:

fn main() {
    loop {
        println!("again!");
        break;  // Exit the loop
    }
}

Returning Values from Loops

fn main() {
    let mut counter = 0;
    
    let result = loop {
        counter += 1;
        
        if counter == 10 {
            break counter * 2;  // Return value from loop
        }
    };
    
    println!("The result is {}", result);  // 20
}

Loop Labels

Disambiguate between nested loops:

fn main() {
    let mut count = 0;
    'counting_up: loop {
        println!("count = {}", count);
        let mut remaining = 10;
        
        loop {
            println!("remaining = {}", remaining);
            if remaining == 9 {
                break;  // Break inner loop
            }
            if count == 2 {
                break 'counting_up;  // Break outer loop
            }
            remaining -= 1;
        }
        
        count += 1;
    }
    println!("End count = {}", count);
}

While Loops

Conditional looping:

fn main() {
    let mut number = 3;
    
    while number != 0 {
        println!("{}!", number);
        number -= 1;
    }
    
    println!("LIFTOFF!!!");
}

For Loops

Iterate over collections:

fn main() {
    let a = [10, 20, 30, 40, 50];
    
    for element in a {
        println!("the value is: {}", element);
    }
}

Range

fn main() {
    // 1 to 4 (exclusive end)
    for number in 1..5 {
        println!("{}", number);
    }
    
    // 1 to 5 (inclusive end)
    for number in 1..=5 {
        println!("{}", number);
    }
    
    // Reverse
    for number in (1..4).rev() {
        println!("{}", number);
    }
}

Iterating with Index

fn main() {
    let a = [10, 20, 30, 40, 50];
    
    for (index, value) in a.iter().enumerate() {
        println!("a[{}] = {}", index, value);
    }
}

Pattern Matching

The match Expression

fn main() {
    let number = 3;
    
    match number {
        1 => println!("One!"),
        2 => println!("Two!"),
        3 => println!("Three!"),
        4 => println!("Four!"),
        5 => println!("Five!"),
        _ => println!("Something else"),  // _ is catch-all
    }
}

Match with Values

fn value_in_cents(coin: &str) -> u32 {
    match coin {
        "penny" => 1,
        "nickel" => 5,
        "dime" => 10,
        "quarter" => 25,
        _ => 0,
    }
}

fn main() {
    println!("Value: {}", value_in_cents("dime"));
}

Match Must Be Exhaustive

fn main() {
    let number = 3;
    
    // ERROR: non-exhaustive patterns
    match number {
        1 => println!("one"),
        2 => println!("two"),
    }  // Missing other cases!
}

Fix: Add _ catch-all or handle all cases.

Multiple Patterns

fn main() {
    let number = 2;
    
    match number {
        1 | 2 => println!("one or two"),
        3 | 4 | 5 => println!("three, four, or five"),
        _ => println!("something else"),
    }
}

Range Patterns

fn main() {
    let number = 5;
    
    match number {
        1..=5 => println!("one through five"),
        6..=10 => println!("six through ten"),
        _ => println!("something else"),
    }
}

Matching Tuples

fn main() {
    let point = (0, 5);
    
    match point {
        (0, 0) => println!("origin"),
        (0, y) => println!("on y-axis at {}", y),
        (x, 0) => println!("on x-axis at {}", x),
        (x, y) => println!("point at ({}, {})", x, y),
    }
}

Match Guards

Additional conditions:

fn main() {
    let num = Some(4);
    
    match num {
        Some(x) if x < 5 => println!("less than five: {}", x),
        Some(x) => println!("{}", x),
        None => (),
    }
}

If Let

Concise way to match one pattern:

fn main() {
    let some_u8_value = Some(3);
    
    // Using match
    match some_u8_value {
        Some(3) => println!("three"),
        _ => (),
    }
    
    // Using if let (more concise)
    if let Some(3) = some_u8_value {
        println!("three");
    }
}

If Let with Else

fn main() {
    let some_value = Some(7);
    
    if let Some(7) = some_value {
        println!("lucky seven!");
    } else {
        println!("not seven");
    }
}

While Let

fn main() {
    let mut stack = Vec::new();
    stack.push(1);
    stack.push(2);
    stack.push(3);
    
    while let Some(top) = stack.pop() {
        println!("{}", top);
    }
}

Common Patterns

Checking Multiple Conditions

fn classify_number(n: i32) {
    match (n > 0, n % 2 == 0) {
        (true, true) => println!("positive and even"),
        (true, false) => println!("positive and odd"),
        (false, true) => println!("negative or zero and even"),
        (false, false) => println!("negative or zero and odd"),
    }
}

Early Returns

fn process_number(n: i32) -> i32 {
    if n < 0 {
        return 0;
    }
    
    if n > 100 {
        return 100;
    }
    
    n * 2
}

Loop with Conditional Break

fn find_first_even(numbers: &[i32]) -> Option<i32> {
    for &num in numbers {
        if num % 2 == 0 {
            return Some(num);
        }
    }
    None
}

Practice Exercises

Exercise 1: FizzBuzz

Print numbers 1 to 100:

  • "Fizz" for multiples of 3
  • "Buzz" for multiples of 5
  • "FizzBuzz" for multiples of both
  • The number otherwise
fn main() {
    for n in 1..=100 {
        // Your code here
    }
}

Exercise 2: Find Maximum

Write a function that finds the maximum value in a slice:

fn find_max(numbers: &[i32]) -> Option<i32> {
    // Return None if empty
    // Your code here
}

Exercise 3: Temperature Category

Write a function that categorizes temperature:

  • "Freezing" < 0°C
  • "Cold" 0-10°C
  • "Mild" 11-20°C
  • "Warm" 21-30°C
  • "Hot" > 30°C

Key Takeaways

  • if is an expression that returns a value
  • loop creates infinite loops, use break to exit
  • while loops while condition is true
  • for is best for iterating over collections
  • match must be exhaustive and handles all patterns
  • if let is concise for matching single patterns
  • Loop labels disambiguate nested loops

Next Steps

In the next chapter, we'll explore functions in depth, including closures and methods.

Quick Reference

// If expression
let x = if condition { 5 } else { 6 };

// Loop with return
let result = loop {
    break value;
};

// While loop
while condition {
    // code
}

// For loop
for item in collection {
    // code
}

// Range
for i in 0..10 { }      // 0 to 9
for i in 0..=10 { }     // 0 to 10

// Match
match value {
    pattern1 => result1,
    pattern2 => result2,
    _ => default,
}

// If let
if let Some(x) = option {
    // use x
}