7.3 Loops

Rust offers several looping constructs, some of which are similar to C’s, while others (like loop) have no direct C counterpart. Rust also lacks a do-while loop, but you can emulate that behavior using loop combined with condition checks and break.

7.3.1 The loop Construct

loop creates an infinite loop unless you explicitly break out of it:

fn main() {
    let mut count = 0;
    loop {
        println!("Count is: {}", count);
        count += 1;
        if count == 5 {
            break;
        }
    }
}

Key Points:

  • Infinite by Default: You must use break to exit.
  • Expression-Friendly: A loop can return a value via break.

Loops as Expressions

fn main() {
    let mut count = 0;
    let result = loop {
        count += 1;
        if count == 10 {
            break count * 2;
        }
    };
    println!("The result is: {}", result);
}

When count reaches 10, the break expression returns count * 2 (which is 20) to result.

7.3.2 The while Loop

A while loop executes as long as its condition evaluates to true. This mirrors C’s while loop but enforces Rust’s strict type safety by requiring a boolean condition—implicit conversions from non-boolean values are not allowed.

Basic while Loop Example

fn main() {
    let mut count = 0;
    while count < 5 {
        println!("Count is: {}", count);
        count += 1;
    }
}

This loop runs while count < 5, incrementing count on each iteration.

while as an Expression

In Rust, loops can return values using break expr;. Thus, a while loop can serve as an expression that evaluates to a final value when exiting via break.

Example: Using while as an Expression

fn main() {
    let mut n = 1;
    let result = while n < 10 {
        if n * n > 20 {
            break n;  // The loop returns 'n' when this condition is met
        }
        n += 1;
    };

    println!("Loop returned: {:?}", result);
}

Here, the while loop assigns a value to result. When n * n > 20, the loop exits via break n;, making result hold the final value of n.

7.3.3 The for Loop

Rust’s for loop iterates over ranges or collections rather than offering the classic three-part C-style for loop:

fn main() {
    for i in 0..5 {
        println!("i is {}", i);
    }
}

Key Points:

  • Range Syntax: 0..5 includes 0, 1, 2, 3, and 4, but excludes 5.
  • Inclusive Range: 0..=5 includes 5 as well.
  • Iterating Collections: You can directly iterate over arrays, vectors, and slices.
fn main() {
    let numbers = [10, 20, 30];
    for number in numbers {
        println!("Number is {}", number);
    }
}

7.3.4 Labeled break and continue in Nested Loops

Rust allows you to label loops and then use break or continue with these labels, which is particularly handy for nested loops:

fn main() {
    'outer: for i in 0..3 {
        for j in 0..3 {
            if i == j {
                continue 'outer;
            }
            if i + j == 4 {
                break 'outer;
            }
            println!("i = {}, j = {}", i, j);
        }
    }
}
  • Labels: Defined with a leading single quote (for example, 'outer).
  • Targeted Control: break 'outer; stops the outer loop, while continue 'outer; skips to the next iteration of the outer loop.

In C, achieving similar behavior often requires extra flags or the use of goto, which can be less clear and more error-prone.