DEV Community

Cover image for [Rust Guide] 3.6. Control Flow - Loops
SomeB1oody
SomeB1oody

Posted on

[Rust Guide] 3.6. Control Flow - Loops

3.6.0. Before We Begin

Welcome to Chapter 3 of this Rust self-study series. It has 6 sections:

  • Variables and Mutability
  • Data Types: Scalar Types
  • Data Types: Compound Types
  • Functions and Comments
  • Control Flow: if else
  • Control Flow: Loops (this article)

Through the guessing game in Chapter 2 (beginners who have not read it are strongly encouraged to take a look), you should now have learned the basic Rust syntax. In Chapter 3, we will go one level deeper and learn the general programming concepts in Rust.

If you find this helpful, please like, bookmark, and follow. To keep learning along, follow this series.

3.6.1. Loops in Rust

Rust provides three kinds of loops:

  • loop
  • while
  • for

3.6.2. The loop Loop

The loop keyword tells Rust to keep executing a block of code over and over until told to stop. Here is an example; it will keep printing 6657 up up!.

fn main(){
    loop {
        println!("6657 up up!");
    }
}
Enter fullscreen mode Exit fullscreen mode

You can use the break keyword inside a loop to tell the program when to stop.

fn main(){
    let mut counter = 0;
    let result = loop {
        counter += 1;

        if counter == 10 {
            break counter * 2;
        }
    };
    println!("The result is: {}", result);
}
Enter fullscreen mode Exit fullscreen mode

Code logic:

  • counter is initialized to 0 and increments by 1 on each loop.
  • When counter equals 10, break exits the loop and returns counter * 2 (that is, 20).
  • loop is an expression, and its return value is the value passed to break, so it can be assigned directly to result.
  • result is finally printed as 20.

Code features:

  • Rust's loop is an expression, so its result can be bound directly to a variable.
  • break can carry a return value (here, counter * 2) and use it as the result of the loop.
  • A let statement requires a semicolon after the assignment expression, so the closing brace } of the loop must be followed by a semicolon.

3.6.3. while Conditional Loops

The while loop checks its condition before each execution of the loop body.

fn main() {
    let mut countdown = 10; // Start the countdown at 10

    println!("Rocket Launch Countdown:");

    while countdown > 0 {
        println!("T-minus {}...", countdown);
        countdown -= 1; // Decrease by 1 each time
    }

    println!("🚀 Liftoff!");
    println!("Houston, we have a problem.");
}
Enter fullscreen mode Exit fullscreen mode

This is a simple while loop example, and its output is:

Rocket Launch Countdown:
T-minus 10...
T-minus 9...
T-minus 8...
T-minus 7...
T-minus 6...
T-minus 5...
T-minus 4...
T-minus 3...
T-minus 2...
T-minus 1...
🚀 Liftoff!
Houston, we have a problem.
Enter fullscreen mode Exit fullscreen mode

3.6.4. Using for Loops to Traverse Collections

Of course, you can also use while and loop to iterate over a collection, but that is error-prone and inefficient.
Here is an example using while:

fn main() {
    let numbers = [10, 20, 30, 40, 50];
    let mut index = 0;

    println!("Using while loop:");
    while index < 5 {
        println!("Number at index {}: {}", index, numbers[index]);
        index += 1;
    }
}
Enter fullscreen mode Exit fullscreen mode

When using while, it is very easy to trigger a panic from an out-of-bounds index, and it also runs more slowly because the condition index < 5 must be checked every time.

Here is an example using for that achieves the same result:

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

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

1. numbers.iter()

  • Calls the .iter() method on the collection numbers to create an immutable iterator that visits the elements one by one. In Rust, a for loop does not operate on the collection directly; it operates on an iterator that implements the Iterator trait. .iter() is a commonly used method on Vec and other collections that produces an iterator of references to the elements. for loops are concise and clear, and they can run code for every element in a collection. Because of their safety and simplicity, they are used the most in Rust.

2. .enumerate()
• Attaches an index to each element of the iterator. The index starts at 0 and is a usize value. .enumerate() wraps each element of the iterator into a (index, value) form, where index is the element's position in the collection and value is the current element pointed to by the iterator. .enumerate() returns a new iterator whose item type is (usize, &T), where T is the type of the elements in the collection. Here, numbers is an array of i32, so &T is &i32.

3. for (index, number) in ...
• The for loop supports destructuring tuples. (index, number) means that we directly destructure the (usize, &T) tuple produced by enumerate() into two variables: index, the current element's index; and number, the current element's reference (immutable).

Suppose numbers is [10, 20, 30, 40, 50]; the execution flow is as follows:

  1. Call numbers.iter() to create an iterator.
  2. Call .enumerate() to produce an iterator of (index, element reference) pairs.
  3. The for loop destructures the index and the element:
    • First iteration: index = 0, number = &10
    • Second iteration: index = 1, number = &20
    • Third iteration: index = 2, number = &30
    • ...
  4. Print index and number to output each element's index and value.

Because for loops are safe and concise, they are used the most in Rust.

3.6.5. Range

Range is provided by the standard library. You can use Range to generate numbers between two bounds (excluding the end). The rev method can be used to reverse a Range.

fn main() {
    println!("Rocket Launch Countdown:");

    for countdown in (1..=10).rev() {
        println!("T-minus {}...", countdown);
    }

    println!("🚀 Liftoff!");
    println!("Houston, we have a problem.");
}
Enter fullscreen mode Exit fullscreen mode

This example uses for loops, Range, and rev to implement the rocket countdown shown in the while example above.

Code breakdown

  1. (1..=10):
    • This is a Range representing numbers from 1 to 10, inclusive.
    • ..= is the inclusive upper-bound range operator.
  2. .rev():
    • Reverses the iterator, producing a descending sequence from 10 down to 1.

Top comments (0)