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");
}
}
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.
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.
&& 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
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));
}
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).
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.
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.
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!!!
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
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)