Mastering Control Flow in Rust
Tags: rust, tutorial, beginners, programming
Published: 2024-03-15 18:30:00

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:
```rust
fn main() {
    let number = 42;

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

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:
```rust
fn main() {
    let number = 3;
    if number {
        // mismatched types, expected `bool`, found integer
        println!("number was three");
    }
}
```

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

### 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:
```rust
fn main() {
    let is_even = if number % 2 == 0 { true } else { false };
    println!("Is the number even? {}", is_even);
}
```

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

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

### 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:
```rust
fn main() {
    let number = 42;
    let result = if number > 50 { "greater" } else { "less or equal" };
    println!("Number is {}", result);
}
```

## Repetition with Loops: Controlling Iteration in Rust

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

Example:
```rust
fn main() {
    let mut count = 0;

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

        if count == 5 {
            break; // Exit the loop when count reaches 5
        }
    }
}
```

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

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

    let result = loop {
        counter += 1;

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

    println!("The result is {result}"); // 20
}
```

### `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:
```rust
'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
```

### `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:
```rust
fn main() {
    let mut number = 1;

    while number <= 5 {
        println!("Number: {}", number);
        number += 1;
    }
}
```

### `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:
```rust
fn main() {
    for number in (1..=3).rev() {
        // rev(), to reverse the range
        println!("{number}!");
    }
    println!("LIFTOFF!!!"); // Output: 3 2 1 "LIFTOFF!!!"
}
```

Example with iterable object (e.g., array):
```rust
fn main() {
    let numbers = [1, 2, 3, 4, 5];

    for number in numbers.iter() {
        println!("Number: {}", number);
    }
}
```

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:
```rust
fn main() {
    let a = [10, 20, 30, 40, 50];

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

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`:
```rust
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
        }
    }
}
```

## 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.