Iterators
enumerate()
The .enumerate() adapter pairs each element yielded by an iterator with its zero-based iteration count, returning a std::iter::Enumerate<Self> iterator whose Item is (usize, Self::Item).
Signature
fn enumerate(self) -> Enumerate<Self>
where
Self: Sized,
// where Enumerate<I>: Iterator<Item = (usize, <I as Iterator>::Item)>
Source: [ "alpha", "beta", "gamma" ]
│ │ │ .enumerate()
▼ ▼ ▼
Output: [ (0, "alpha"), (1, "beta"), (2, "gamma") ]
fn main() {
let stages = ["compile", "test", "package"];
for (idx, stage) in stages.iter().enumerate() {
println!("Step {}: {stage}", idx + 1);
}
}
Step 1: compile
Step 2: test
Step 3: package
Ordering Matters: .filter().enumerate() vs. .enumerate().filter()
Because .enumerate() assigns sequential 0, 1, 2, ... counters to whatever items reach it:
- Placing
.enumerate()before.filter()preserves each item's original index in the source sequence. - Placing
.enumerate()after.filter()numbers only the kept items consecutively starting from0.
fn main() {
let words = ["keep", "skip", "keep", "skip", "keep"];
// 1. Original source indices: .enumerate() THEN .filter()
let original_indices: Vec<(usize, &str)> = words
.into_iter()
.enumerate()
.filter(|&(_idx, word)| word == "keep")
.collect();
// 2. New consecutive indices: .filter() THEN .enumerate()
let consecutive_indices: Vec<(usize, &str)> = words
.into_iter()
.filter(|&word| word == "keep")
.enumerate()
.collect();
println!("Original indices: {original_indices:?}");
println!("Consecutive indices: {consecutive_indices:?}");
}
Original indices: [(0, "keep"), (2, "keep"), (4, "keep")]
Consecutive indices: [(0, "keep"), (1, "keep"), (2, "keep")]