Nugget 12

Home

Iterator Adapters

map() and filter() get all the attention, but the iterator toolbox has far more — for slicing, combining, pairing, reversing, and stepping through sequences with precision.

Slicing: take() and skip()

take(n) yields the first n items and stops. skip(n) ignores the first n items and yields the rest:

let nums = 1..=10;

// take — first 3 items
let first_three: Vec<_> = nums.clone().take(3).collect();
assert_eq!(first_three, vec![1, 2, 3]);

// skip — everything after the first 7
let after_seven: Vec<_> = nums.clone().skip(7).collect();
assert_eq!(after_seven, vec![8, 9, 10]);

// combined: pagination
let page: Vec<_> = nums.clone().skip(4).take(3).collect();
assert_eq!(page, vec![5, 6, 7]);
// skip 4 items → start at 5, take 3 → 5, 6, 7

💡 Safe on short iterators

take(100) on a 5-item iterator just yields all 5 — no panic. skip(100) on a 5-item iterator yields nothing — also no panic.

Indexing: enumerate()

enumerate() pairs each item with its index (starting at 0). This is the iterator-friendly replacement for C-style index loops:

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

for (i, fruit) in fruits.iter().enumerate() {
    println!("#{i}: {fruit}");
}
// #0: apple
// #1: banana
// #2: cherry

// With filter
let fruits = ["apple", "avocado", "banana", "apricot", "cherry"];
for (i, fruit) in fruits.iter()
    .enumerate()
    .filter(|(_, name)| name.starts_with("a"))
{
    println!("fruit starting with 'a' at index {i}");
}
// fruit starting with 'a' at index 0
// fruit starting with 'a' at index 1
// fruit starting with 'a' at index 3

📐 enumerate() captures position

Note: enumerate() records the index before any subsequent filter() — so the index matches the original position, not the filtered position. If you put enumerate after filter, indices reset.

Pairing: zip()

zip() combines two iterators into one, producing a tuple of items side-by-side. It stops when either iterator runs out:

let names = ["Alice", "Bob", "Charlie"];
let scores = [95, 87, 92];

let paired: Vec<(&str, &i32)> = names.iter()
    .zip(scores.iter())
    .collect();
// [("Alice", 95), ("Bob", 87), ("Charlie", 92)]

// Uneven lengths — stops at the shorter
let short = [1, 2];
let long = [10, 20, 30, 40];
let zipped: Vec<_> = short.iter().zip(long.iter()).collect();
assert_eq!(zipped, vec![(&1, &10), (&2, &20)]);  // 30 and 40 dropped

A common pattern: zip with enumerate and the source to create a HashMap:

use std::collections::HashMap;

let keys = ["x", "y", "z"];
let values = [10, 20, 30];
let map: HashMap<&str, &i32> = keys.iter()
    .zip(values.iter())
    .collect();
// {"x": 10, "y": 20, "z": 30}

Concatenating: chain()

chain() appends one iterator after another:

let first = [1, 2, 3];
let second = [4, 5, 6];

let combined: Vec<_> = first.iter()
    .chain(second.iter())
    .collect();
// [1, 2, 3, 4, 5, 6]

// Chaining more than two
let a = [1, 2];
let b = [3, 4];
let c = [5, 6];
let all: Vec<_> = a.iter()
    .chain(b.iter())
    .chain(c.iter())
    .collect();
// [1, 2, 3, 4, 5, 6]

Stepping and Reversing

// step_by — every nth element
let evens: Vec<_> = (0..=10).step_by(2).collect();
assert_eq!(evens, vec![0, 2, 4, 6, 8, 10]);

// rev — reverse (needs a double-ended iterator)
let backwards: Vec<_> = (1..=5).rev().collect();
assert_eq!(backwards, vec![5, 4, 3, 2, 1]);

// Practical: read lines reversed
let text = "first\nsecond\nthird";
for line in text.lines().rev() {
    println!("{line}");
}
// third
// second
// first

Putting It Together

The real power comes from combining adapters. Here's a "leaderboard" that sorts scores, skips the bottom 3, and prints the top entries with ranks:

use std::collections::HashMap;

let scores = HashMap::from([
    ("Alice", 95),
    ("Bob", 72),
    ("Charlie", 88),
    ("Diana", 91),
    ("Eve", 67),
]);

// Top 3, ranked
let mut ranked: Vec<(&str, &i32)> = scores.iter().collect();
ranked.sort_by(|a, b| b.1.cmp(a.1));  // sort by score descending

// Now with iterators:
ranked.iter()
    .take(3)
    .enumerate()
    .for_each(|(i, (name, score))| {
        println!("#{}: {} — {}", i + 1, name, score);
    });
// #1: Alice — 95
// #2: Diana — 91
// #3: Charlie — 88

Key Takeaways