๐Ÿฆ€ Functional Rust
๐ŸŽฌ How Rust Iterators Work Lazy evaluation, chaining, collect(), and zero-cost abstractions.
๐Ÿ“ Text version (for readers / accessibility)

โ€ข Iterators are lazy โ€” .map(), .filter(), .take() build a chain but do no work until consumed

โ€ข .collect() triggers evaluation, transforming the chain into a Vec, HashMap, or other collection

โ€ข Zero-cost abstraction: iterator chains compile to the same machine code as hand-written loops

โ€ข .iter() borrows, .into_iter() consumes, .iter_mut() borrows mutably

โ€ข Chaining replaces nested loops with a readable, composable pipeline

258: Index-Value Pairs with enumerate()

Difficulty: 1 Level: Beginner Get `(index, value)` pairs from any iterator without tracking a counter manually.

The Problem This Solves

You're iterating a list and you need the position of each element โ€” to number output lines, filter by index, or find which position holds a value. The naive solution is a mutable `let mut i = 0;` counter that you increment at the bottom of every loop body. That counter can drift, get forgotten after a `continue`, or just clutter the code. In Python you reach for `enumerate(items)`. In OCaml you use `List.iteri` or `List.mapi`. In Rust, `enumerate()` is the idiomatic answer โ€” it works on any iterator, not just slices, and integrates cleanly with the rest of the adapter chain. The bigger payoff comes in chains. Without `enumerate()` you can't easily combine index-awareness with `filter`, `map`, or `find` โ€” you'd need to `collect()` first, then use index-based loops. With `enumerate()` you stay in the lazy pipeline.

The Intuition

`enumerate()` wraps each element with its zero-based position, turning `Iterator<Item=T>` into `Iterator<Item=(usize, T)>`.
let fruits = ["apple", "banana", "cherry"];
for (i, fruit) in fruits.iter().enumerate() {
 // i = 0, 1, 2
}

How It Works in Rust

let fruits = ["apple", "banana", "cherry", "date"];

// Basic: loop with position
for (i, fruit) in fruits.iter().enumerate() {
 println!("{}: {}", i, fruit);
}

// Filter by index โ€” keep only even positions
let even_indexed: Vec<_> = fruits.iter()
 .enumerate()
 .filter(|(i, _)| i % 2 == 0)  // pattern-match the tuple
 .map(|(_, v)| *v)               // drop the index again
 .collect();

// Format with 1-based numbers
let numbered: Vec<String> = fruits.iter()
 .enumerate()
 .map(|(i, f)| format!("{}. {}", i + 1, f))  // i+1 for 1-based
 .collect();

// Find first matching element AND its position
let found = fruits.iter()
 .enumerate()
 .find(|(_, f)| f.starts_with('c'));  // โ†’ Some((2, "cherry"))
Destructure the tuple immediately in the closure signature โ€” it's cleaner than `pair.0`/`pair.1`.

What This Unlocks

Key Differences

ConceptOCamlRust
Iterate with index`List.iteri (fun i x -> ...)``iter.enumerate()`
Map with index`List.mapi (fun i x -> ...)``.enumerate().map(\(i, x)\...)`
Filter with index`List.filteri (fun i _ -> ...)``.enumerate().filter(\(i, _)\...)`
Index type`int``usize` (always non-negative)
Works on any iteratorNo โ€” list-specific functionsYes โ€” any `Iterator`
//! 258. Index-value pairs with enumerate()
//!
//! `enumerate()` adds a zero-based index to every iterator element.

fn main() {
    let fruits = ["apple", "banana", "cherry", "date"];

    for (i, fruit) in fruits.iter().enumerate() {
        println!("{}: {}", i, fruit);
    }

    let even_indexed: Vec<_> = fruits.iter()
        .enumerate()
        .filter(|(i, _)| i % 2 == 0)
        .map(|(_, v)| *v)
        .collect();
    println!("Even-indexed: {:?}", even_indexed);

    let numbered: Vec<String> = fruits.iter()
        .enumerate()
        .map(|(i, f)| format!("{}. {}", i + 1, f))
        .collect();
    println!("Numbered: {:?}", numbered);

    let found = fruits.iter()
        .enumerate()
        .find(|(_, f)| f.starts_with('c'));
    println!("First 'c' word: {:?}", found);

    let scores = [88u32, 72, 95, 60];
    let grades: Vec<(usize, &str)> = scores.iter()
        .enumerate()
        .map(|(i, &s)| (i, if s >= 90 { "A" } else if s >= 80 { "B" } else { "C" }))
        .collect();
    println!("Grades: {:?}", grades);
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_enumerate_indices() {
        let v = ["a", "b", "c"];
        let indices: Vec<usize> = v.iter().enumerate().map(|(i, _)| i).collect();
        assert_eq!(indices, vec![0, 1, 2]);
    }

    #[test]
    fn test_enumerate_values() {
        let v = [10i32, 20, 30];
        let result: Vec<i32> = v.iter()
            .enumerate()
            .map(|(i, &val)| val + i as i32)
            .collect();
        assert_eq!(result, vec![10, 21, 32]);
    }

    #[test]
    fn test_enumerate_filter_even() {
        let v = ["a", "b", "c", "d"];
        let even: Vec<_> = v.iter()
            .enumerate()
            .filter(|(i, _)| i % 2 == 0)
            .map(|(_, v)| *v)
            .collect();
        assert_eq!(even, vec!["a", "c"]);
    }
}
(* 258. Index-value pairs with enumerate() - OCaml *)

let () =
  let fruits = ["apple"; "banana"; "cherry"] in
  List.iteri (fun i fruit ->
    Printf.printf "%d: %s\n" i fruit
  ) fruits;

  let evens_only = List.filteri (fun i _ -> i mod 2 = 0) fruits in
  Printf.printf "Even indices: %s\n" (String.concat ", " evens_only);

  let indexed_names = List.mapi (fun i name ->
    Printf.sprintf "#%d %s" (i + 1) name
  ) fruits in
  List.iter print_endline indexed_names