Start learning free

LESSON 154 OF 414 · LOOPING OVER ARRAY INDICES

Looping Through Arrays With Indices in Rust

Lesson Read the full explanation

In some instances, we will want to loop through an array using indices.

The indices of an array meals are all the integer values from 0 to meals.len() - 1, so we can iterate over them using an index variable i:

let meals = vec!["pancakes", "pasta", "pizza", "avocado"];

for i in 0..meals.len() {
	println!("Meal number {} is {}.", i + 1, meals[i]);
}

The output of this code is:

Meal number 1 is pancakes.
Meal number 2 is pasta.
Meal number 3 is pizza.
Meal number 4 is avocado.

Assignment
Now let's print the temperature on each day, in this format:

On day 1 the temperature is 10.

On day 2 the temperature is 60.

...


Hint
Look at the examples above if you get stuck.


STEP 1 OF 2
    Ctrl + Enter to check

    RUST · LOOPING OVER ARRAY INDICES

    Looping Through Arrays With Indices in Rust, explained

    What looping with indices means

    A vector of n elements has the valid indices 0 through n - 1. Looping over indices means the loop variable is a position, not a value, and you read the element with vec[i]. This is the tool to reach for when the position itself matters: printing "number 3 of 4", comparing neighbouring elements, changing elements in place, or returning where something was found.

    Rust's ordinary for x in &vec hands you the elements directly and doesn't tell you where you are. When you need the position, loop over a range of indices.

    The syntax

    The range 0..meals.len() produces 0, 1, 2, ... up to but not including the length. That is exactly the set of valid indices:

    let meals = vec!["pancakes", "pasta", "pizza", "avocado"];
    
    for i in 0..meals.len() {
        println!("Meal number {} is {}.", i + 1, meals[i]);
    }
    

    The output:

    Meal number 1 is pancakes.
    Meal number 2 is pasta.
    Meal number 3 is pizza.
    Meal number 4 is avocado.
    

    Three details make this work:

    • The range is half-open. a..b includes a and stops before b. Because len() is one more than the last index, 0..meals.len() never reaches an index that doesn't exist.
    • The index is a usize. len() returns a usize, so the range, and therefore i, has that type. Rust indexes vectors only with usize, so meals[i] needs no conversion.
    • Printing with println!. Each {} in the format string is replaced by the next argument, in order. Here the first {} gets i + 1 and the second gets meals[i]. Humans count from 1 while indices start at 0, so the loop prints i + 1.

    Common mistakes

    • An inclusive range. 0..=meals.len() includes the length itself. On a vector of 4 elements the last iteration reads index 4, and the program panics with "index out of bounds: the len is 4 but the index is 4". Use .., not ..=, with len().
    • Subtracting from an empty length. The form 0..v.len() - 1 is a trap. Since len() is a usize, an empty vector gives 0 - 1. In an optimized build, which the lessons use, that wraps around to 18446744073709551615; in a debug build the subtraction panics. Either way it isn't the empty loop you wanted. Use 0..v.len(), or 1..v.len() when you want to skip the first element.
    • Forgetting the +1 for display. Without it the days would be numbered 0, 1, 2, 3.
    • Using the index where the value belongs. Printing i instead of meals[i] shows positions instead of the data.
    • Expecting a negative index to work. An index can't be negative: a usize has no negative values, so meals[-1] doesn't compile.

    An empty vector needs no special care. 0..0 is an empty range, so the loop body never runs.

    An alternative: enumerate

    When you need both the position and the element and you don't index into other places, enumerate() gives them together:

    for (i, meal) in meals.iter().enumerate() {
        println!("Meal number {} is {}.", i + 1, meal);
    }
    

    This prints the same four lines. The index loop is still the more general tool: it lets you read v[i + 1], step by two, or change v[i] in place.

    The exercise

    The starter code gives a vector temperatures holding 10, 60, -5, 32. The task is to print the temperature of each day in the form On day 1 the temperature is 10.

    1. Write the loop. Use an index i over the range 0..temperatures.len(), so i takes every valid index of temperatures.
    2. Print inside the loop. Use println! with the format string "On day {} the temperature is {}.". The first placeholder gets i + 1, the day number, and the second gets temperatures[i].
    let temperatures = vec![10, 60, -5, 32];
    
    for i in 0..temperatures.len() {
        println!("On day {} the temperature is {}.", i + 1, temperatures[i]);
    }
    

    The output:

    On day 1 the temperature is 10.
    On day 2 the temperature is 60.
    On day 3 the temperature is -5.
    On day 4 the temperature is 32.
    

    The vector has no type annotation. Rust infers Vec<i32> from the integer literals, and a negative value like -5 prints with its sign.

    Key takeaways

    • The valid indices of a vector are 0 to len() - 1, and the range 0..v.len() covers exactly those.
    • Ranges are half-open: the end is excluded.
    • Indices are usize values and can't go below zero, so avoid subtracting from len() when the vector may be empty.
    • Reading past the end panics at run time instead of returning garbage.
    • Loop over indices when you need the position; loop over the elements when you don't.
    Keep going with RustAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free