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..bincludesaand stops beforeb. Becauselen()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 ausize, so the range, and thereforei, has that type. Rust indexes vectors only withusize, someals[i]needs no conversion. - Printing with
println!. Each{}in the format string is replaced by the next argument, in order. Here the first{}getsi + 1and the second getsmeals[i]. Humans count from 1 while indices start at 0, so the loop printsi + 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..=, withlen(). - Subtracting from an empty length. The form
0..v.len() - 1is a trap. Sincelen()is ausize, an empty vector gives0 - 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. Use0..v.len(), or1..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
iinstead ofmeals[i]shows positions instead of the data. - Expecting a negative index to work. An index can't be negative: a
usizehas no negative values, someals[-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.
- Write the loop. Use an index
iover the range0..temperatures.len(), soitakes every valid index oftemperatures. - Print inside the loop. Use
println!with the format string"On day {} the temperature is {}.". The first placeholder getsi + 1, the day number, and the second getstemperatures[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
0tolen() - 1, and the range0..v.len()covers exactly those. - Ranges are half-open: the end is excluded.
- Indices are
usizevalues and can't go below zero, so avoid subtracting fromlen()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.
