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 from 0.
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")]