ZetCode

Rust control flow

last modified October 18, 2023

In this article we show how to control the flow of a Rust program.

The code of a Rust program run from top to bottom. The flow of the program can be altered with various keywords, including if/else, loop, for, while, and match.

The control flow structures are used to executed code conditionally or multiple times.

[dependencies]
rand = "0.8.5"

For generating random values, we need the rand package.

Rust if condition

The if keyword is used to create simple conditional tests. It can be used in conjuction with the else if and else keyword.

main.rs
use rand::Rng;

fn main() {
    let mut rng = rand::thread_rng();
    let n = rng.gen_range(-5..5);

    if n < 0 {
        println!("negative value");
    } else if n == 0 {
        println!("zero");
    } else {
        println!("positive value");
    }
}

We randomly select a value from a range -5..5. Based on the resulting value, we print a message. There are three possible branches that can be executed.

if n < 0 {
    println!("negative value");
...

If the expression following the if keyword is true, the next statement is executed. Other branches are not executed.

} else if n == 0 {
    println!("zero");
...

If the previous branch was not true, we try the next branch; the else if. If it is true, it's block is executed and the testing is finished. If not, we continue to the next branch.

} else {
    println!("positive value");
}

The else branch is always executed when the previous ones fail.

λ cargo run -q
negative value
λ cargo run -q
positive value
λ cargo run -q
zero
λ cargo run -q
positive value

The if conditions are expressions; they can return values.

main.rs
use rand::Rng;

fn main() {

    let mut rng = rand::thread_rng();

    let n = rng.gen_range(-5..5);

    let msg = if n < 0 {
        "negative value"
    } else if n == 0 {
        "zero"
    } else {
        "positive value"
    };

    println!("{}", msg);
}

In this example, the if expression returns a result which is later printed.

Rust loop

An infinite loop is created with the loop keyword. The loop is terminated with the break keyword.

main.rs
fn main() {
    let mut i = 0;

    loop {
        println!("falcon");

        i += 1;
        if i == 5 {
            break;
        }
    }
}

In the example, we print a word five times.

let mut i = 0;

We define a counter; the i is often used as a variable for a counter.

loop {
    println!("falcon");

    i += 1;
    if i == 5 {
        break;
    }
}

With loop, we start executing the given code block until we break the loop.

if i == 5 {
    break;
}

If the condition is met, the loop is terminated with break.

λ cargo run -q
falcon
falcon
falcon
falcon
falcon

Rust while loop

The while keyword is used to create a loop. It runs until the given condition is met.

main.rs
fn main() {
    let mut x = 1;

    while x <= 10 {
        println!("{}", x);
        x += 1;
    }

    println!("x: {}", x);
}

In the program, we print numbers 1 through 10. In the end, we print the x, which is used as a counter.

while x <= 10 {
    println!("{}", x);
    x += 1;
}

The while loop is executed while the condition is true; that is, x is less than 10.

λ cargo run -q
1
2
3
4
5
6
7
8
9
10
x: 11

In the next example we use a while loop to calculate a sum of integers.

main.rs
use std::vec;

fn main() {
    let vals = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

    let mut i = 0;
    let mut sum = 0;
    let n = vals.len();

    while i < n {
        sum += vals[i];
        i += 1;
    }

    println!("The sum is: {}", sum);
}

We have a vector of integers. Using while loop, we calculate the sum of the elements.

let mut i = 0;
let mut sum = 0;
let n = vals.len();

In order to calculate the sum, we need an index variable, the sum variable, and the length of the vector.

while i < n {
    sum += vals[i];
    i += 1;
}

In the while loop, we add the elements to the sum variable.

λ cargo run -q
The sum is: 55

Rust for loop

With the for keyword, we iterate over a range or collection of values.

main.rs
fn main() {
    for i in 1..=10 {
        print!("{} ", i);
    }

    println!();

    let vals = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

    for e in vals {
        print!("{} ", e);
    }

    println!();
}

In the program, we iterate over a range of integers and an array of integers.

for i in 1..=10 {
    print!("{} ", i);
}

We go through the integers of a range; in each cycle, the i variable holds the current value. We print the value in the block.

let vals = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

for e in vals {
    print!("{} ", e);
}

We have an array of integers; we go through the array element by element and print the values to the terminal.

λ cargo run -q
1 2 3 4 5 6 7 8 9 10 
1 2 3 4 5 6 7 8 9 10 

Rust match expression

Pattern matching is a powerful control flow construct that allows us to compare a value against a series of patterns and then execute code based on which pattern matches.

In match expressions, each option that is executed is called an arm.

main.rs
fn main() {
    let grades = ["A", "B", "C", "D", "E", "F", "FX"];

    for grade in grades {
        match grade {
            "A" | "B" | "C" | "D" | "E" | "F" => println!("passed"),
            "FX" => println!("failed"),
            _ => println!("unknown")
        }
    }
}

We have an array of grades. We go through the array and print "passed" or "failed" for each value. This example uses a multiple option arm, which saves a lot of space. It is much shorter than using several if/else keywords.

$ cargo run -q
passed
passed
passed
passed
passed
passed
failed

In this article we have worked with control flow structures in Rust.

Author

My name is Jan Bodnar and I am a passionate programmer with many years of programming experience. I have been writing programming articles since 2007. So far, I have written over 1400 articles and 8 e-books. I have over eight years of experience in teaching programming.