Skip to content

Control Flow

The most common constructs that let you control the flow of execution of Rust code are if expressions and loops.

if Expressions

An if expression allows you to branch your code depending on conditions.

rust
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");
    }
}

A few points to note:

  • The condition in if expression must be a bool. Unlike languages such as Ruby and JavaScript, Rust will not automatically try to convert non-Boolean types to a Boolean.
  • If you don’t provide an else expression and the condition is false, the program will just skip the if block and move on to the next bit of code.
  • Using too many else if expressions can clutter your code, so if you have more than one, you might want to refactor your code. Consider using a powerful Rust branching construct called match for these cases. This is covered later.
  • Blocks of code associated with the conditions in if expressions are sometimes called arms, just like the arms in match expressions.

You can separate multiple conditions in if expression using logical operators (&&, ||, !). Example:

rust
fn main() {
    let temperature = 25;
    let is_sunny = true;

    if temperature > 20 && is_sunny {
        println!("It's warm and sunny — perfect for going outside!");
    } else if temperature > 20 && !is_sunny {
        println!("It's warm but cloudy — maybe take an umbrella.");
    } else if temperature <= 20 || !is_sunny {
        println!("It’s either cool or not sunny — maybe stay in.");
    } else {
        println!("Default case (this probably won't happen).");
    }
}

Using if in a let Statement

Because if is an expression, we can use it on the right side of a let statement to assign the outcome to a variable. Example:

rust
fn main() {
    let condition = true;
    let number = if condition { 5 } else { 6 };

    println!("The value of number is: {number}");
}

It's important to note that the arms of the if expression should have the same type, else the code will run into a compilation error.

Loops

It’s often useful to execute a block of code more than once. For this purpose, Rust provides three kinds of loops: loop, while, and for.

loop

The loop keyword tells Rust to execute a block of code over and over again forever or until you explicitly tell it to stop.

rust
fn main() {
    loop {
        println!("again!");
    }
}

You can stop the execution of this program manually, using the keyboard shortcut ctrl-c to interrupt a program. The interrupt signal may or may not be honoured, depending on where the code was in the loop when it received the signal.

break and continue

Rust also provides a way to break out of a loop using code. You can place the break keyword within the loop to tell the program when to stop executing the loop.

In case, you want your program to skip over any remaining code in this iteration of the loop and go to the next iteration, you can use continue instead.

Note that, break and continue keywords can also be used with while and for loops.

Returning values from loops

One of the uses of a loop is to retry an operation you know might fail, such as checking whether a thread has completed its job. You might also need to pass the result of that operation out of the loop to the rest of your code. To do this, you can add the value you want returned after the break expression you use to stop the loop; that value will be returned out of the loop so you can use it, as shown here:

rust
fn main() {
    let mut counter = 0;

    let result = loop {
        counter += 1;

        if counter == 10 {
            break counter * 2;
        }
    };

    println!("The result is {result}");
}

Note that you can also return from inside a loop. While break only exits the current loop, return always exits the current function.

while

Sometimes a program needs to keep running code while a certain condition is true. When the condition becomes false, the loop stops.

You could write this using a loop with if, else, and break, but Rust provides a simpler construct for that: the while loop.

rust
fn main() {
    let mut number = 3;

    while number != 0 {
        println!("{number}!");
        number -= 1;
    }

    println!("LIFTOFF!!!");
}

Here, the while loop exits when number becomes 0.

You can also use while to loop over array elements:

rust
fn main() {
    let a = [10, 20, 30, 40, 50];
    let mut index = 0;

    while index < 5 {
        println!("the value is: {}", a[index]);
        index += 1;
    }
}

But it’s error-prone - if the array length changes and you forget to update the condition, your program could panic by going out of bounds. It’s also slightly slower, because Rust checks the index each time through the loop. Rust provides a safe and concise alternative for this: the for loop.

for

Rust’s for loop lets you safely iterate over collections without worrying about indexes:

rust
fn main() {
    let a = [10, 20, 30, 40, 50];

    for element in a {
        println!("the value is: {element}");
    }
}

The safety and conciseness of for loops make them the most commonly used loop construct in Rust. Even in situations in which you want to run some code a certain number of times, most Rustaceans would use a for loop. Here’s the countdown example rewritten with a range and the rev() method to reverse it:

rust
fn main() {
    for number in (1..4).rev() {
        println!("{number}!");
    }
    println!("LIFTOFF!!!");
}

Loop Labels

If you have loops within loops, break and continue apply to the innermost loop at that point. You can optionally specify a loop label on a loop that you can then use with break or continue to specify that those keywords apply to the labeled loop instead of the innermost loop. Loop labels must begin with a single quote. Here’s an example with two nested loops:

rust
fn main() {
    'outer: loop {
        loop {
            println!("Inside inner loop");
            break 'outer; // exits the outer loop
        }
        println!("This never prints");
    }
    println!("Done!");
}

Note that loop labels can be used with all loop types in Rust.