Site

Control Flow — Rust Execution Control

Tutorial 5.0  •  Rust / Learn

5.0 What This Teaches

This tutorial covers every construct Rust provides for directing the order of execution:

5.1 if / else

if selects a branch based on a boolean condition. Rust does not coerce integers or other types to bool - the condition must be exactly bool.
let temp = 72;

if temp > 80 {
    println!("hot");
} else if temp > 60 {
    println!("comfortable");
} else {
    println!("cold");
}

5.2 if as an Expression

Because if is an expression in Rust, it can produce a value. Both arms must evaluate to the same type - the compiler enforces this at compile time.
let description = if temp > 60 { "warm" } else { "cool" };
println!("temp is {description}");
This is more direct than declaring a mutable variable and reassigning it inside each branch. Use it whenever you are choosing between two values of the same type.

5.3 loop

loop runs its body forever. You exit it with break. Unlike while true, loop signals intent clearly: "this runs until I explicitly stop it." break can carry a value out of the loop, making loop usable as an expression:
let mut n = 0;
let result = loop {
    n += 1;
    if n == 5 {
        break n * 2;  // result receives 10
    }
};
println!("loop result = {result}");
This pattern is useful when computing a value requires retrying - for example, reading user input or waiting for a condition to become true.

5.4 while

while checks a condition before each iteration and stops when it becomes false.
let mut count = 3;
while count > 0 {
    println!("count = {count}");
    count -= 1;
}
Use while when the number of iterations depends on a condition that changes inside the loop body. Use for when iterating over a known sequence.

5.5 for over a Range

Ranges give a concise way to iterate over integers. 0..4 is exclusive of the upper bound (produces 0, 1, 2, 3). 1..=3 is inclusive (produces 1, 2, 3).
for i in 0..4 {
    print!("{i} ");
}
println!();  // flush the line
for i in 1..=3 {
    print!("{i} ");
}
println!();
print! (without ln) writes without a trailing newline. The final println!() with no arguments writes just a newline, completing the line.

5.6 for over a Slice

for works over any iterator, including slices. The & before the array name borrows it - you are reading the elements, not consuming or moving them.
let words = ["alpha", "beta", "gamma"];
for word in &words {
    println!("{word}");
}
If you need the index alongside the value, use .iter().enumerate():
for (i, word) in words.iter().enumerate() {
    println!("{i}: {word}");
}

5.7 match

match compares a value against a list of patterns and runs the first arm that matches. It is exhaustive - the compiler requires every possible value to be covered. The wildcard _ catches anything not matched by an earlier arm.
let score = 85;
let grade = match score {
    90..=100 => "A",
    80..=89  => "B",
    70..=79  => "C",
    _        => "below C",
};
println!("grade = {grade}");
Each arm is pattern => expression. A comma ends each arm (the last comma is optional). match is an expression - here the matched string slice is assigned directly to grade. match is preferred over long if/else if chains when testing a single value against multiple cases. The compiler warns about unreachable arms and errors if any value is unhandled.

5.8 Example - All Together

// Control_Flow - demonstrates if/else, loop, while, for, and match in Rust.

fn main() {
    let temp = 72;
    if temp > 80 {
        println!("hot");
    } else if temp > 60 {
        println!("comfortable");
    } else {
        println!("cold");
    }

    let description = if temp > 60 { "warm" } else { "cool" };
    println!("temp is {description}");

    let mut n = 0;
    let result = loop {
        n += 1;
        if n == 5 { break n * 2; }
    };
    println!("loop result = {result}");

    let mut count = 3;
    while count > 0 {
        println!("count = {count}");
        count -= 1;
    }

    for i in 0..4 { print!("{i} "); }
    println!();

    for i in 1..=3 { print!("{i} "); }
    println!();

    let words = ["alpha", "beta", "gamma"];
    for word in &words { println!("{word}"); }

    let score = 85;
    let grade = match score {
        90..=100 => "A",
        80..=89  => "B",
        70..=79  => "C",
        _        => "below C",
    };
    println!("grade = {grade}");
}
Expected output:
comfortable
temp is warm
loop result = 10
count = 3
count = 2
count = 1
0 1 2 3
1 2 3
alpha
beta
gamma
grade = B

5.9 Exercise

Exercise
  • Write a for loop over the range 1..=10 that prints each number, but prints "fizz" instead when the number is divisible by 3 and "buzz" when divisible by 5. Use if/else if/else inside the loop body.
  • Use loop and break to find the first integer greater than 100 that is divisible by 7. Print it. Assign the result using break value so you do not need a separate variable.
  • Rewrite the grade computation from section 5.7 using if/ else if/else as an expression instead of match. Which reads more clearly for this case?

5.10 Common Mistakes

Non-boolean condition in if

let x = 1;
if x { ... }  // error: expected bool, found integer
Rust does not treat non-zero as true. Write if x != 0 { ... }.

Mismatched types in if expression arms

let v = if condition { 1 } else { "one" };  // error: mismatched types
Both arms must produce the same type. Here 1 is i32 and "one" is &str.

Exclusive vs. inclusive range confusion

for i in 0..5 { ... }   // iterates 0, 1, 2, 3, 4  (5 is excluded)
for i in 0..=5 { ... }  // iterates 0, 1, 2, 3, 4, 5
.. excludes the upper bound; ..= includes it.

Non-exhaustive match

let x: i32 = 3;
match x {
    1 => println!("one"),
    2 => println!("two"),
    // error: pattern `i32::MIN..=0_i32` and more not covered
}
Add a _ => ... arm or cover every possible value.

Forgetting & when iterating a slice

let nums = [1, 2, 3];
for n in nums { ... }   // moves the array; fine for Copy types like i32
for n in &nums { ... }  // borrows; works for any type and is more general
For non-Copy types (like String), iterating without & moves the elements out of the array, which is usually not what you want.

5.11 Key Terms

TermMeaning
if expressionA branch construct that can produce a value when both arms have the same type
loopAn unconditional loop that runs until break
break valueExits a loop and delivers value as the loop expression's result
whileA loop that checks a boolean condition before each iteration
forIterates over a range or any other iterator
range 0..nProduces integers from 0 up to but not including n
range 0..=nProduces integers from 0 through n inclusive
matchExhaustive pattern matching; every possible value must be handled
wildcard _A match arm pattern that matches anything not covered by earlier arms
.enumerate()Iterator adapter that pairs each element with its index