2.8 Control Flow Constructs

Rust provides standard control flow structures, but with some differences compared to C, particularly regarding conditions and loops.

2.8.1 Conditional Execution with if, else if, and else

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

As in C, Rust uses % for the modulo operation and == to test for equality.

  • Conditions must evaluate to a bool. Unlike C, integers are not automatically treated as true (non-zero) or false (zero).
  • Parentheses () around the condition are not required.
  • Curly braces {} around the blocks are mandatory, even for single statements, preventing potential dangling else issues.
  • if is an expression in Rust, meaning it can return a value:
    fn main() {
        let condition = true;
        let number = if condition { 5 } else { 6 }; // `if` as an expression
        println!("The number is {}", number);
    }

2.8.2 Repetition: loop, while, and for

Rust offers three looping constructs:

  • loop: Creates an infinite loop, typically exited using break. break can also return a value from the loop.

    fn main() {
        let mut counter = 0;
        let result = loop {
            counter += 1;
            if counter == 10 {
                break counter * 2; // Exit loop and return counter * 2
            }
        };
        println!("The loop result is {}", result); // Prints 20
    }
  • while: Executes a block as long as a boolean condition remains true.

    fn main() {
        let mut number = 3;
        while number != 0 {
            println!("{}!", number);
            number -= 1;
        }
        println!("LIFTOFF!!!");
    }
  • for: Iterates over elements produced by an iterator. This is the most common and idiomatic loop in Rust. It’s fundamentally different from C’s typical index-based for loop.

    fn main() {
        // Iterate over a range (0 to 4)
        for i in 0..5 {
            println!("The number is: {}", i);
        }
    
        // Iterate over elements of an array
        let a = [10, 20, 30, 40, 50];
        // `.iter()` creates an iterator over references; inferred since Rust 2021
        for element in a { // or explicitly `a.iter()`
            println!("The value is: {}", element);
        }
    }

    There is no direct equivalent to C’s for (int i = 0; i < N; ++i) construct in Rust. Range-based for loops or explicit iterator usage are preferred for safety and clarity.

  • continue: Skips the rest of the current iteration and proceeds to the next one, usable in all loop types.

2.8.3 Control Flow Comparisons with C

  • Rust enforces bool conditions in if and while. C allows integer conditions (0 is false, non-zero is true).
  • Rust requires braces {} for if/else/while/for blocks. C allows omitting them for single statements, which can be error-prone.
  • Rust’s for loop is exclusively iterator-based. C’s for loop is a general structure with initialization, condition, and increment parts.
  • Rust prevents assignments within if conditions (e.g., if x = y { ... } is an error), avoiding a common C pitfall (if (x = y) vs. if (x == y)).
  • Rust has match, a powerful pattern-matching construct (covered later) that is often more versatile than C’s switch.