Mastering Control Flow in Rust

iamdipankarpaul

Dipankar Paul

Posted on March 15, 2024

Mastering Control Flow in Rust

In Rust, understanding control flow is crucial for writing efficient and safe code. The language provides powerful constructs such as if expressions and loops to control the flow of operations based on conditions. Let's dive into these concepts to understand their usage and nuances.

if Expressions: Making Decisions in Rust

Basic if-else

In Rust, if expressions are used to execute code based on conditions. An if block must have a condition that evaluates to a boolean value (true or false). If the condition is true, the code inside the if block is executed; otherwise, the code inside the else block is executed.

Example:

fn main() {
    let number = 42;

    if number > 50 {
        println!("Number is greater than 50");
    } else {
        println!("Number is less than or equal to 50");
    }
}
Enter fullscreen mode Exit fullscreen mode

It’s also worth noting that the condition in this code must be a bool. If the condition isn’t a bool, we’ll get an error.

Example:

fn main() {
    let number = 3;
    if number {
        // mismatched types, expected `bool`, found integer
        println!("number was three");
    }
}
Enter fullscreen mode Exit fullscreen mode

if-else if ladder

Rust allows chaining multiple conditions using else if, creating a hierarchical decision-making structure. The conditions are evaluated in order, and the corresponding block of code for the first true condition is executed.

Example:

fn main() {
    let grade = 85;

    if grade >= 90 {
        println!("Grade A");
    } else if grade >= 80 {
        println!("Grade B");
    } else if grade >= 70 {
        println!("Grade C");
    } else {
        println!("Grade below C");
    }
}
Enter fullscreen mode Exit fullscreen mode

Using if in Assignments

In Rust, if expressions can be used in assignments, allowing you to assign values based on conditions. The value assigned is the result of the executed block based on the condition.

Example:

fn main() {
    let is_even = if number % 2 == 0 { true } else { false };
    println!("Is the number even? {}", is_even);
}
Enter fullscreen mode Exit fullscreen mode

Note that the return values from each arm of the if must be the same type, or we’ll get an error.

Example:

fn main() {
    let condition = true;
    let number = if condition { 5 } else { "six" };
    // `if` and `else` have incompatible types
    // expected integer, found `&str`
    println!("The value of number is: {number}");
}
Enter fullscreen mode Exit fullscreen mode

Ternary Operator (Shorthand for Simple if-else)

Although Rust does not have a traditional ternary operator, you can achieve similar functionality using a shorthand with if-else expressions.

Example:

fn main() {
    let number = 42;
    let result = if number > 50 { "greater" } else { "less or equal" };
    println!("Number is {}", result);
}
Enter fullscreen mode Exit fullscreen mode

Repetition with Loops: Controlling Iteration in Rust

loop

The loop keyword creates an infinite loop, which continues until explicitly interrupted using break.

Example:

fn main() {
    let mut count = 0;

    loop {
        println!("Count: {}", count);
        count += 1;

        if count == 5 {
            break; // Exit the loop when count reaches 5
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

You can also use loop to return a value out of the loop.

Example:

fn main() {
    let mut counter = 0;

    let result = loop {
        counter += 1;

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

    println!("The result is {result}"); // 20
}
Enter fullscreen mode Exit fullscreen mode

loop inside loop:

You can create nested loops by placing one loop inside another one. But in this case, break and continue will apply to the innermost loop at that point.

To avoid this problem, rust have something called loop labels. Loop labels allow you to name loops, and then use the break or continue statements with a specific label to indicate which loop you want to affect.

Loop labels must begin with a single quote.

Example:

'outer: loop {
    // Code inside the outer loop
    'inner: loop {
        // Code inside the inner loop

        if some_condition {
            break 'inner;
        }

        if another_condition {
            break 'outer;
        }
    }
    // Code after the inner loop
}
// Code after the labeled loops
Enter fullscreen mode Exit fullscreen mode

while

The while loop executes a block of code as long as a specified condition is true. It is useful when the number of iterations is not known beforehand.

Example:

fn main() {
    let mut number = 1;

    while number <= 5 {
        println!("Number: {}", number);
        number += 1;
    }
}
Enter fullscreen mode Exit fullscreen mode

for

The for loop iterates over a range, a collection, or an iterable object, executing the block of code for each iteration. It is commonly used with arrays, ranges, and iterators.

Example with range:

fn main() {
    for number in (1..=3).rev() {
        // rev(), to reverse the range
        println!("{number}!");
    }
    println!("LIFTOFF!!!"); // Output: 3 2 1 "LIFTOFF!!!"
}
Enter fullscreen mode Exit fullscreen mode

Example with iterable object (e.g., array):

fn main() {
    let numbers = [1, 2, 3, 4, 5];

    for number in numbers.iter() {
        println!("Number: {}", number);
    }
}
Enter fullscreen mode Exit fullscreen mode

The .iter() method is used to create an iterator over the elements of the array numbers. The loop then iterates over each element using this iterator. Using .iter(), does not consume the array; it only borrows the elements. This means the array numbers can still be used after the loop.

Example without iterator:

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

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

Here, the array a is directly used in the loop. In this case, the loop consumes the array, and each iteration takes ownership of each element of the array. If the elements of the array were of a type that does not implement the Copy trait, this approach would move the elements out of the array, and the array a would be unusable after the loop.

We will learn about the Ownership in the upcoming blogs.

Loop Control Statements

Rust provides break and continue statements to control the flow within loops. break exits the loop, while continue skips the rest of the current iteration and proceeds to the next one.

Example with break and continue:

fn main() {
    for number in 1..=10 {
        if number % 2 == 0 {
            continue; // Skip even numbers
        }

        println!("Number: {}", number);

        if number == 7 {
            break; // Exit the loop when number is 7
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

Understanding control flow constructs such as if expressions and loops is essential for writing efficient and robust Rust code. These constructs enable you to make decisions and control the flow of your programs, allowing for complex logic and iteration. Mastering these concepts will empower you to write clear, concise, and reliable Rust code.

💖 💪 🙅 🚩
iamdipankarpaul
Dipankar Paul

Posted on March 15, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related