Why not index by hand
A loop such as for i in 0..meals.len() with meals[i] inside works, but Rust prefers iterators, which give you both the position and the item at once with no way to run past the end. Two adapters cover the common cases: enumerate() counts the items while you loop, and zip() walks two vectors side by side.
enumerate: items with their index
enumerate() gives each item together with its index, as a tuple. The loop unpacks the tuple into two names, the way let (name, age) = pet does.
let fruits = vec!["apple", "kiwi", "plum"];
for (i, fruit) in fruits.iter().enumerate() {
println!("{}. {}", i + 1, fruit);
}
1. apple
2. kiwi
3. plum
The index comes first and starts at 0, so print i + 1 for a numbered list. The index is a usize.
zip: two vectors side by side
a.iter().zip(b.iter()) pairs the first item of a with the first item of b, then the second items, and so on. Each pair is a tuple.
let prices = vec![3, 1, 2];
for (fruit, price) in fruits.iter().zip(prices.iter()) {
println!("{} {}", fruit, price);
}
apple 3
kiwi 1
plum 2
The items are references, because iter() borrows. println! prints them as they are; for arithmetic, write *price.
zip stops at the end of the shorter vector, and the extra items are skipped with no error. Zipping three players with two numbers gives two pairs.
Using both together
To number the pairs, call enumerate() on the zipped iterator. Each item is then an index and a pair, so the names nest in the same shape:
for (i, (fruit, price)) in fruits.iter().zip(prices.iter()).enumerate() {
println!("{}. {} costs {}", i + 1, fruit, price);
}
1. apple costs 3
2. kiwi costs 1
3. plum costs 2
The order of the calls decides the shape. zip(...).enumerate() gives (i, (a, b)).
Other things to loop over
enumerate() works on any iterator, not only on vectors. On the characters of a string, "abc".chars().enumerate() gives (0, 'a'), (1, 'b') and (2, 'c').
A pair of values also invites a calculation. Zipping two vectors and multiplying the pairs gives a dot product: with [1, 2, 3] and [4, 5, 6], the expression a.iter().zip(b.iter()).map(|(x, y)| x * y).sum::<i32>() is 32. The closure unpacks each pair right in its parameter list, with the same pattern a for loop uses.
Common mistakes
- Index second. The pattern is
(i, item). Writing(item, i)puts the item in the index's place and the compiler reports a type problem when you use them. - Starting the count at 0 in a numbered list. Print
i + 1, as above. - Forgetting the nesting. After
zip(...).enumerate(), the pattern(i, fruit, price)does not match; it must be(i, (fruit, price)). - Expecting an error from
zipon uneven lengths. It quietly stops at the shorter one, so a missing score would drop a player from the output without a warning.
The exercise, step by step
A quiz show keeps its players in players and their points in scores, in the same order. The task prints the players as a numbered list, then each player with their points.
- First loop:
for (i, player) in players.iter().enumerate(). The index comes first. - Print
i + 1, a dot, a space and the player, such as1. Ana. The index starts at 0, so adding 1 makes Ana number 1. - Second loop:
for (player, points) in players.iter().zip(scores.iter()), which pairs Ana with 12, Ben with 9 and Cleo with 15. - Print each player, the word
has, the points and the wordpoints. No index is needed here.
fn main_function() {
let players = vec!["Ana", "Ben", "Cleo"];
let scores = vec![12, 9, 15];
for (i, player) in players.iter().enumerate() {
println!("{}. {}", i + 1, player);
}
for (player, points) in players.iter().zip(scores.iter()) {
println!("{} has {} points", player, points);
}
}
The output:
1. Ana
2. Ben
3. Cleo
Ana has 12 points
Ben has 9 points
Cleo has 15 points
Key takeaways
enumerate()yields(index, item), with the index first, starting at 0.zip()yields pairs of items by position and stops at the shorter vector.- Together,
zip(...).enumerate()yields(index, (a, b)), so unpack it with nested parentheses. - With
iter()the items are references; use*for arithmetic.
