Iterating over collections in Rust: iter, iter_mut and into_iter
The three ways to loop over a Vec, array or HashMap in Rust: &v (iter), &mut v (iter_mut) and v (into_iter), plus enumerate, zip and rev.
There are three ways to loop over a collection, and they differ in what you get for each element and what happens to the collection:
| Loop | Same as | Each item is | Collection afterwards |
|---|---|---|---|
for x in &v |
v.iter() |
&T (shared borrow) |
unchanged, still usable |
for x in &mut v |
v.iter_mut() |
&mut T (can modify) |
modified, still usable |
for x in v |
v.into_iter() |
T (owned) |
moved: gone |
fn main() {
let mut prices = vec![100, 250, 40];
for p in &prices {
print!("{p} ");
}
println!();
for p in &mut prices {
*p += 5;
}
println!("{prices:?}");
for p in prices {
print!("{} ", p * 2);
}
println!();
}
100 250 40
[105, 255, 45]
210 510 90
Use &v by default. Use &mut v to change elements in place (with *x to reach the value). Use v when you're done with the collection and want to move its elements somewhere else, like into another collection or a thread.
Index and value together: enumerate
Instead of for i in 0..v.len() with v[i], write for (i, x) in v.iter().enumerate(). It's clearer, can't go out of bounds, and clippy suggests it (needless_range_loop). Other everyday adapters:
.rev(): backwards..zip(other): pairs from two collections, stopping at the shorter one..skip(n),.take(n): skip or keep the firstn..windows(2): overlapping pairs from a slice (great for differences between neighbors)..chunks(3): groups of up to 3.
Maps and sets
Iterating a HashMap gives (&K, &V) tuples: for (name, score) in &scores. Use .keys() or .values() for one side, .values_mut() to change values. The order is unspecified, so sort the entries (or use BTreeMap) before printing.
Arrays and strings
Arrays work like vectors: for x in [1, 2, 3] takes items by value (since the 2021 edition), for x in &arr borrows. A String isn't directly iterable: choose .chars() (characters), .bytes(), .lines() or .split_whitespace().
Common mistakes
- Using the collection after
for x in v: E0382, "borrow of moved value:v". Loop over&v. - Modifying through a shared borrow:
for x in &v { *x += 1; }is E0594, "cannot assign to*x, which is behind a&reference". Use&mut v. - Forgetting
*withiter_mut:
fn main() {
let mut v = vec![1, 2, 3];
for x in &mut v {
x += 1;
}
println!("{v:?}");
}
error[E0368]: binary assignment operation `+=` cannot be applied to type `&mut {integer}`
Write *x += 1.
- Pushing to a collection while looping over it: E0502. Collect changes first, then apply them.
Example
use std::collections::HashMap;
fn main() {
let names = vec!["Ferris", "Corro", "Gopher"];
let mut scores = vec![72, 95, 88];
for (i, name) in names.iter().enumerate() {
println!("{}. {name}", i + 1);
}
for s in &mut scores {
*s += 5;
}
for (name, score) in names.iter().zip(&scores) {
println!("{name}: {score}");
}
for pair in scores.windows(2) {
println!("change {:+}", pair[1] - pair[0]);
}
let mut by_name: HashMap<&str, i32> = HashMap::new();
for (name, score) in names.iter().zip(scores.iter()) {
by_name.insert(name, *score);
}
let mut keys: Vec<_> = by_name.keys().collect();
keys.sort();
for k in keys {
println!("{k} -> {}", by_name[k]);
}
let total: i32 = scores.into_iter().sum(); // consumes scores
println!("total {total}");
}
Output:
1. Ferris
2. Corro
3. Gopher
Ferris: 77
Corro: 100
Gopher: 93
change +23
change -7
Corro -> 100
Ferris -> 77
Gopher -> 93
total 270