Iterator::enumerate() / zip() / flat_map()
| Since: | Rust 1.0(2015) |
|---|
Rust iterators include enumerate() for indexed iteration, zip() for combining two iterators, and flat_map() for transforming and flattening nested iterators.
Syntax
for (i, val) in vec!["a", "b", "c"].iter().enumerate() {
println!("{}: {}", i, val);
}
// zip(): combines two iterators into an iterator of tuples.
let keys = vec!["a", "b", "c"];
let vals = vec![1, 2, 3];
let pairs: Vec<(_, _)> = keys.iter().zip(vals.iter()).collect();
// flat_map(): transform and flatten the results.
let words = vec!["user_a user_c", "user_e user_b"];
let chars: Vec<&str> = words.iter().flat_map(|s| s.split_whitespace()).collect();
// flatten(): flatten a nested iterator by one level.
let nested = vec![vec![1, 2], vec![3, 4], vec![5]];
let flat: Vec<i32> = nested.into_iter().flatten().collect();
Method Reference
| Method | Description |
|---|---|
| iter.enumerate() | Returns an iterator of (index, value) tuples. |
| iter.zip(other) | Combines two iterators into an iterator of (a, b) tuples. |
| iter.flat_map(|x| ...) | Flattens the result when the mapping closure returns an iterator. |
| iter.flatten() | Flattens a nested iterator by one level. |
| iter.chain(other) | Concatenates two iterators into a single iterator. |
| iter.take(n) | Takes only the first n elements. |
| iter.skip(n) | Skips the first n elements. |
| iter.step_by(n) | Takes every nth element. |
| iter.rev() | Returns a reversed iterator (requires DoubleEndedIterator). |
| iter.peekable() | Returns an iterator that lets you peek at the next element without consuming it. |
Sample Code
sample_iter_enumerate_zip_flatmap.rs
fn main() {
// enumerate(): loop with index.
let pilots = vec!["user_a", "user_c", "user_e"];
for (i, pilot) in pilots.iter().enumerate() {
println!("{}: {}", i, pilot);
}
// enumerate() to build a numbered list.
let numbered: Vec<String> = pilots.iter()
.enumerate()
.map(|(i, p)| format!("{}. {}", i + 1, p))
.collect();
println!("numbered: {:?}", numbered);
// zip(): combine two Vecs.
let names = vec!["user_a", "user_c", "user_e"];
let scores = vec![85, 90, 92];
let combined: Vec<(_, _)> = names.iter().zip(scores.iter()).collect();
println!("zip: {:?}", combined);
// zip() to pair names with BGM tracks.
let bgms = vec!["track_x", "track_y", "track_z"];
let name_bgm: Vec<(_, _)> = names.iter().zip(bgms.iter()).collect();
println!("name-bgm: {:?}", name_bgm);
// zip() to build a HashMap.
use std::collections::HashMap;
let map: HashMap<_, _> = names.iter().zip(scores.iter()).collect();
println!("zip to HashMap: {:?}", map);
// flat_map(): split each string into words and flatten.
let sentences = vec!["user_a user_c user_e", "user_b user_d"];
let words: Vec<&str> = sentences.iter().flat_map(|s| s.split_whitespace()).collect();
println!("flat_map words: {:?}", words);
// flatten(): flatten nested Vecs.
let nested = vec![vec![1, 2, 3], vec![4, 5], vec![6]];
let flat: Vec<i32> = nested.into_iter().flatten().collect();
println!("flatten: {:?}", flat);
// chain(): concatenate two iterators.
let a = vec![1, 2, 3];
let b = vec![4, 5, 6];
let chained: Vec<i32> = a.iter().chain(b.iter()).copied().collect();
println!("chain: {:?}", chained);
// take() and skip().
let v: Vec<i32> = (1..=10).collect();
let first3: Vec<_> = v.iter().take(3).collect();
let skip3: Vec<_> = v.iter().skip(3).collect();
println!("take(3): {:?}", first3);
println!("skip(3): {:?}", skip3);
}
Compile with the following command:
rustc iter_enumerate_zip_flatmap.rs
./iter_enumerate_zip_flatmap
0: user_a
1: user_c
2: user_e
numbered: ["1. user_a", "2. user_c", "3. user_e"]
zip: [("user_a", 85), ("user_c", 90), ("user_e", 92)]
name-bgm: [("user_a", "track_x"), ("user_c", "track_y"), ("user_e", "track_z")]
zip to HashMap: {"user_a": 85, "user_c": 90, "user_e": 92}
flat_map words: ["user_a", "user_c", "user_e", "user_b", "user_d"]
flatten: [1, 2, 3, 4, 5, 6]
chain: [1, 2, 3, 4, 5, 6]
take(3): [1, 2, 3]
skip(3): [4, 5, 6, 7, 8, 9, 10]
Common Mistakes
Common mistake 1: zip() silently truncates at the shorter iterator
zip() stops as soon as the shorter of the two iterators is exhausted. Elements from the longer iterator are silently dropped.
fn main() {
let names = vec!["user_a", "user_b", "user_c", "user_d"]; // 4 elements
let scores = vec![90, 85, 75]; // 3 elements
let pairs: Vec<_> = names.iter().zip(scores.iter()).collect();
println!("{:?}", pairs);
// [("user_a", 90), ("user_b", 85), ("user_c", 75)]
// "user_d" is silently dropped
println!("original: {}, zipped: {}", names.len(), pairs.len());
// original: 4, zipped: 3
}
Common mistake 2: using map() when flat_map() is needed
map() wraps the closure's return value as-is. flat_map() flattens one level of nesting. Use flat_map() when the closure returns an iterator or collection.
fn main() {
let sentences = vec!["user_a user_b", "user_c user_d"];
// map() produces Vec<Vec<&str>> — nested
let nested: Vec<Vec<&str>> = sentences.iter()
.map(|s| s.split_whitespace().collect())
.collect();
println!("{:?}", nested); // [["user_a", "user_b"], ["user_c", "user_d"]]
// flat_map() produces Vec<&str> — flattened
let flat: Vec<&str> = sentences.iter()
.flat_map(|s| s.split_whitespace())
.collect();
println!("{:?}", flat); // ["user_a", "user_b", "user_c", "user_d"]
}
Overview
zip() stops when the shorter iterator is exhausted, so it handles iterators of different lengths safely. If you need all elements from both sides, use zip_longest() from the itertools crate.
flat_map() is equivalent to map().flatten(). It is also handy for processing nested Options or Results — for example, iter.flat_map(|x| x.ok()) extracts only the successful values, ignoring errors.
For transforming and filtering, see Iterator::map() / filter().
If you find any errors or copyright issues, please contact us.