Control flow in Rust

Control flow in Rust

if expressions, match, loop expressions, while expressions, for expressions

In this lesson, we'll cover the basic control flow in Rust. We'll cover the following topics:

  • if expressions
  • match
  • loop expressions
  • while expressions
  • for expressions

At the end, we'll have a simple exercise to practice what we've learned.

If you prefer a video version

A recap is available on GitHub (link available in the video description)

Control Flow

Control flow is the order in which different parts of a program are executed.

This is not a concept unique to Rust, but it's a fundamental concept in programming.

Let's start with the most basic example and work our way up.

if expressions

The if expression works just like it does in other languages: it evaluates the condition, and if it’s true, it executes the block of code associated with it. Let’s look at a simple example:

fn main() {
    let number = 3;

    if number < 5 {
        println!("condition was true");
    } else {
        println!("condition was false");
    }
}

Control flow in Rust - Rust programming tutorial

This program prints the condition as true because the value of the number is less than 5. If we change the value of the number to something else, like 6, then the program will print the condition as false instead.

We can also use if in a let statement to bind the value to a variable conditionally. The variable's value would be the value of the expression that matches the if. Here’s an example:

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

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

The number variable will be bound to a value based on the outcome of the if condition. Because the condition is true, the value bound to the number variable is 5. If the condition had been false, the value would have been 6.

Control flow in Rust - Rust programming tutorial

Nested if expressions

We can also use if expressions in a nested manner. For example:

fn main() {
    let num = 15;

    if num % 2 == 0 {
        println!("{} is even", num);
    } else {
        println!("{} is odd", num);

        if num > 10 {
            println!("{} is also greater than 10", num);
        } else {
            println!("{} is not greater than 10", num);
        }
    }
}

If we put 16, the output will be:

16 is even

This is because 16 is even, and the nested if is not executed.

Control flow in Rust - Rust programming tutorial

&& and ||

We can also use && and || to combine multiple conditions. For example:

fn main() {
    let a = 10;
    let b = 5;
    let c = 20;

    // Using && (AND) to check if 'a' is > than 'b' AND 'b' is > than 'c'
    if a > b && b > c {
        println!("a is greater than b and b is greater than c");
    } else {
        println!("Condition with && not met");
    }

    // Using || (OR) to check if 'a' is > than 'b' OR 'b' is > than 'c'
    if a > b || b > c {
        println!("At least one condition with || is met");
    } else {
        println!("The condition with || is not met");
    }
}

In this case, the output will be:

Condition with && not met
At least one condition with || is met

Control flow in Rust - Rust programming tutorial

Match in Rust

The match keyword compares a value against a series of patterns and then executes code based on which pattern matches. Patterns can be made up of literal values, variable names, wildcards, and many other things. The power of match comes from the expressiveness of the patterns and the fact that the compiler confirms that all possible cases are handled.

We'll have a dedicated lesson for match, but here's a simple example

fn main() {

    //with enum, we can define a type with a fixed set of values
    enum Coin {
        Penny,
        Nickel,
        Dime,
        Quarter,
    }

    fn value_in_cents(coin: Coin) -> u8 {
        match coin {
            Coin::Penny => 1,
            Coin::Nickel => 5,
            Coin::Dime => 10,
            Coin::Quarter => 25,
        }
    }

    //define a coin with a random value
    let coin = Coin::Quarter;

    //print the value of the coin
    println!("The value of the coin is: {}", value_in_cents(coin));
}

Control flow in Rust - Rust programming tutorial

Repetition with Loops

Rust has three kinds of loops: loop, while, and for. Let’s cover each one with an example!

Repeating Code with loop

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

fn main() {
    loop {
        print!("Hello, world!");
    }
}

This program will print again! over and over again forever or until you manually stop it (press Ctrl-C or send a SIGINT signal by typing ^C).

Control flow in Rust - Rust programming tutorial

Returning Values from Loops

You can return a value from a loop by adding the value after the break expression you want to return. For example, we can return a value from a loop that prints the values from 1 to 4:

fn main() {
    let mut counter = 0;

    let result = loop {
        counter += 1;

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

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

The break expression will immediately stop a loop. So, if we break with no value, this expression will be equivalent to ().

In this case, the result will be 20, because the loop will stop when the counter is 10, and the value returned will be 10 * 2.

Control flow in Rust - Rust programming tutorial

Conditional Loops with while

Another loop keyword in Rust is while. It behaves similarly to loop, except the condition goes before the loop block.

fn main() {
    let mut counter = 3;

    while counter != 0 {
        println!("{}", counter);

        counter -= 1;
        //wait for 1 second
        std::thread::sleep(std::time::Duration::from_secs(1));
    }

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

This program will print the numbers counting down from 3 to 1, and then it will print LIFTOFF!!!. If we forget to add number -= 1, the program will never exit the while loop because the condition will never be false.

Control flow in Rust - Rust programming tutorial

Looping Through a Collection with for

The for loop is used to loop through each item in a collection. For example, if we wanted to loop through each item in an array of integers and print each item, we could use a for loop, like this:

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

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

the .iter method returns an iterator over the elements of the array.

This program will print each item in the array on its own line:

the value is: 10
the value is: 20
the value is: 30
the value is: 40
the value is: 50

We can also use for to loop over the elements of a string that we’ve previously discussed:

fn main() {
    let s = "hello world";

    for c in s.chars() {
        println!("{}", c);
    }
}

This program will print each character in Hello World on its own line:

h
e
l
l
o

w
o
r
l
d

We can also use for with a range, like this:

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

    println!("go!!!");
}

This program will print the numbers counting down from 1 to 3, and then it will print go!!!

Control flow in Rust - Rust programming tutorial

Exercise - FizzBuzz problem

Last example is the FizzBuzz problem.

The FizzBuzz problem is a classic programming problem often used as an interview question. The task is to print the numbers from 1 to 100. But for multiples of three, print "Fizz" instead of the number, and for the multiples of five, print "Buzz". For numbers which are multiples of both three and five, print "FizzBuzz".

Here's a simple implementation in Rust:

fn main() {
    for number in 1..=100 {
        if number % 3 == 0 && number % 5 == 0 {
            println!("FizzBuzz");
        } else if number % 3 == 0 {
            println!("Fizz");
        } else if number % 5 == 0 {
            println!("Buzz");
        } else {
            println!("{}", number);
        }
    }
}

The last 4 outputs should be:

97
98
Fizz
Buzz

Control flow in Rust - Rust programming tutorial

Summary

That’s all there is to control flow in Rust! You can now write useful programs that do different things based on different conditions and repeat code while a condition holds true.

Here is the code with all the examples:

fn main() {
    //Basic if else
    let number = 3;

    if number < 5 {
        println!("condition was true");
    } else {
        println!("condition was false");
    }

    //let with if else
    let condition = true;
    let number = if condition {
        5
    } else {
        6
    };

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

    //Nested if expressions
    let num = 15;

    if num % 2 == 0 {
        println!("{} is even", num);
    } else {
        println!("{} is odd", num);

        if num > 10 {
            println!("{} is also greater than 10", num);
        } else {
            println!("{} is not greater than 10", num);
        }
    }
    println!("-------------------------------------------");


    //&& and || operators
    let a = 10;
    let b = 5;
    let c = 20;

    // Using && (AND) to check if 'a' is greater than 'b' AND 'b' is greater than 'c'
    if a > b && b > c {
        println!("a is greater than b and b is greater than c");
    } else {
        println!("Condition with && not met");
    }

    // Using || (OR) to check if 'a' is less than 'b' OR 'b' is less than 'c'
    if a > b || b > c {
        println!("At least one condition with || is met");
    } else {
        println!("Neither condition with || is met");
    }
    println!("-------------------------------------------");

    //Match statement
    enum Coin {
        Penny,
        Nickel,
        Dime,
        Quarter,
    }

    fn value_in_cents(coin: Coin) -> u8 {
        match coin {
            Coin::Penny => 1,
            Coin::Nickel => 5,
            Coin::Dime => 10,
            Coin::Quarter => 25,
        }
    }

    let coin = Coin::Quarter;
    println!("The value of the coin is: {}", value_in_cents(coin));
    println!("-------------------------------------------");

    //Infinite loop (commented out)
    // loop {
    //     println!("again!");
    // }

    // Returning a value from a loop
    let mut counter = 0;

    let result = loop {
        counter += 1;

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

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


    //While loop
    let mut counter = 3;

    while counter != 0 {
        println!("{}", counter);

        counter -= 1;
        //wait for 1 second
        std::thread::sleep(std::time::Duration::from_secs(1));
    }

    println!("LIFTOFF!!!");
    println!("-------------------------------------------");

    //For loop to iterate over a collection
    let a = [10, 20, 30, 40, 50];

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

    println!("-------------------------------------------");

    //For loop for elements in a string
    let s = "hello world";

    for c in s.chars() {
        println!("{}", c);
    }
    println!("-------------------------------------------");

    //For loop for elements in a range
    for number in (1..4).rev() {
        println!("{}!", number);
    }
    println!("LIFTOFF!!!");
    println!("-------------------------------------------");

    //FizzBuzz game
    for number in 1..=100 {
        if number % 3 == 0 && number % 5 == 0 {
            println!("FizzBuzz");
        } else if number % 3 == 0 {
            println!("Fizz");
        } else if number % 5 == 0 {
            println!("Buzz");
        } else {
            println!("{}", number);
        }
    }
    println!("-------------------------------------------");
}

Conclusions

In this lesson, we've covered the basic control flow in Rust. We've learned about if expressions, match, loop expressions, while expressions, and for expressions. We've also practiced with a simple exercise.

If you prefer a video version

A recap is available on GitHub (link available in the video description)

Did you find this article valuable?

Support Francesco Ciulla by becoming a sponsor. Any amount is appreciated!