Mastering Rust Loops and Ranges

RustRustBeginner
Practice Now

This tutorial is from open-source community. Access the source code

Introduction

In this lab, we learn about using for loops and ranges in Rust. We can use the for loop along with the range notation a..b to iterate through a range of values. For example, we can write the FizzBuzz program using a for loop instead of a while loop. Additionally, we can use the ..= notation for a range that is inclusive on both ends. The for loop can also interact with iterators in different ways, such as using iter to borrow each element of a collection, into_iter to consume the collection, or iter_mut to mutably borrow each element of the collection. Each of these methods provides a different view of the data within the collection, allowing for different actions to be performed.

Note: If the lab does not specify a file name, you can use any file name you want. For example, you can use main.rs, compile and run it with rustc main.rs && ./main.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL rust(("`Rust`")) -.-> rust/BasicConceptsGroup(["`Basic Concepts`"]) rust(("`Rust`")) -.-> rust/DataTypesGroup(["`Data Types`"]) rust(("`Rust`")) -.-> rust/ControlStructuresGroup(["`Control Structures`"]) rust(("`Rust`")) -.-> rust/FunctionsandClosuresGroup(["`Functions and Closures`"]) rust(("`Rust`")) -.-> rust/DataStructuresandEnumsGroup(["`Data Structures and Enums`"]) rust/BasicConceptsGroup -.-> rust/variable_declarations("`Variable Declarations`") rust/BasicConceptsGroup -.-> rust/mutable_variables("`Mutable Variables`") rust/DataTypesGroup -.-> rust/string_type("`String Type`") rust/ControlStructuresGroup -.-> rust/for_loop("`for Loop`") rust/FunctionsandClosuresGroup -.-> rust/function_syntax("`Function Syntax`") rust/FunctionsandClosuresGroup -.-> rust/expressions_statements("`Expressions and Statements`") rust/DataStructuresandEnumsGroup -.-> rust/method_syntax("`Method Syntax`") subgraph Lab Skills rust/variable_declarations -.-> lab-99308{{"`Mastering Rust Loops and Ranges`"}} rust/mutable_variables -.-> lab-99308{{"`Mastering Rust Loops and Ranges`"}} rust/string_type -.-> lab-99308{{"`Mastering Rust Loops and Ranges`"}} rust/for_loop -.-> lab-99308{{"`Mastering Rust Loops and Ranges`"}} rust/function_syntax -.-> lab-99308{{"`Mastering Rust Loops and Ranges`"}} rust/expressions_statements -.-> lab-99308{{"`Mastering Rust Loops and Ranges`"}} rust/method_syntax -.-> lab-99308{{"`Mastering Rust Loops and Ranges`"}} end

for loops

for and range

The for in construct can be used to iterate through an Iterator. One of the easiest ways to create an iterator is to use the range notation a..b. This yields values from a (inclusive) to b (exclusive) in steps of one.

Let's write FizzBuzz using for instead of while.

fn main() {
    // `n` will take the values: 1, 2, ..., 100 in each iteration
    for n in 1..101 {
        if n % 15 == 0 {
            println!("fizzbuzz");
        } else if n % 3 == 0 {
            println!("fizz");
        } else if n % 5 == 0 {
            println!("buzz");
        } else {
            println!("{}", n);
        }
    }
}

Alternatively, a..=b can be used for a range that is inclusive on both ends. The above can be written as:

fn main() {
    // `n` will take the values: 1, 2, ..., 100 in each iteration
    for n in 1..=100 {
        if n % 15 == 0 {
            println!("fizzbuzz");
        } else if n % 3 == 0 {
            println!("fizz");
        } else if n % 5 == 0 {
            println!("buzz");
        } else {
            println!("{}", n);
        }
    }
}

for and iterators

The for in construct is able to interact with an Iterator in several ways. As discussed in the section on the Iterator trait, by default the for loop will apply the into_iter function to the collection. However, this is not the only means of converting collections into iterators.

into_iter, iter and iter_mut all handle the conversion of a collection into an iterator in different ways, by providing different views on the data within.

  • iter - This borrows each element of the collection through each iteration. Thus leaving the collection untouched and available for reuse after the loop.
fn main() {
    let names = vec!["Bob", "Frank", "Ferris"];

    for name in names.iter() {
        match name {
            &"Ferris" => println!("There is a rustacean among us!"),
            // TODO ^ Try deleting the & and matching just "Ferris"
            _ => println!("Hello {}", name),
        }
    }

    println!("names: {:?}", names);
}
  • into_iter - This consumes the collection so that on each iteration the exact data is provided. Once the collection has been consumed it is no longer available for reuse as it has been 'moved' within the loop.
fn main() {
    let names = vec!["Bob", "Frank", "Ferris"];

    for name in names.into_iter() {
        match name {
            "Ferris" => println!("There is a rustacean among us!"),
            _ => println!("Hello {}", name),
        }
    }

    println!("names: {:?}", names);
    // FIXME ^ Comment out this line
}
  • iter_mut - This mutably borrows each element of the collection, allowing for the collection to be modified in place.
fn main() {
    let mut names = vec!["Bob", "Frank", "Ferris"];

    for name in names.iter_mut() {
        *name = match name {
            &mut "Ferris" => "There is a rustacean among us!",
            _ => "Hello",
        }
    }

    println!("names: {:?}", names);
}

In the above snippets note the type of match branch, that is the key difference in the types of iteration. The difference in type then of course implies differing actions that are able to be performed.

Summary

Congratulations! You have completed the For Loops lab. You can practice more labs in LabEx to improve your skills.

Other Rust Tutorials you may like