Iterator combinators are methods on iterators that allow you to compose and transform sequences of values in powerful ways. They are a cornerstone of functional programming in Rust, enabling you to write concise, expressive, and efficient code without explicit loops.
Rust's Iterator trait provides dozens of combinator methods. These methods take an iterator and return a new iterator with modified behavior. Because they're lazy, combinators don't actually do any work until you consume the iterator (e.g., with collect(), for_each(), or sum()).
let numbers = vec![1, 2, 3, 4, 5];
// chain() combines two iterators into one
let more_numbers = vec![6, 7, 8];
let combined: Vec<i32> = numbers.iter()
.chain(more_numbers.iter())
.copied()
.collect();
// combined = [1, 2, 3, 4, 5, 6, 7, 8]
// zip() pairs elements from two iterators
let names = vec!["Alice", "Bob", "Charlie"];
let ages = vec![30, 25, 35];
let people: Vec<_> = names
.iter()
.zip(ages.iter())
.collect();
// people = [("Alice", 30), ("Bob", 25), ("Charlie", 35)]chain(other) - Concatenates two iterators, yielding all elements from the first, then all from the secondzip(other) - Pairs up elements from two iterators; stops when either is exhaustedtake(n) - Yields only the first n elementsskip(n) - Skips the first n elements, yielding the restrev() - Reverses a double-ended iteratorcycle() - Repeats an iterator infinitely// take() and skip() for slicing
let nums: Vec<i32> = (1..=10).skip(2).take(5).collect();
// nums = [3, 4, 5, 6, 7]
// rev() to reverse
let reversed: Vec<i32> = vec![1, 2, 3]
.into_iter()
.rev()
.collect();
// reversed = [3, 2, 1]
// cycle() for infinite repetition (use with take!)
let pattern: Vec<i32> = [1, 2]
.iter()
.copied()
.cycle()
.take(6)
.collect();
// pattern = [1, 2, 1, 2, 1, 2]Implement the following functions that use iterator combinators:
chain_sequences<T: Clone>(first: &[T], second: &[T]) -> Vec<T>
chain()zip_pairs<T: Clone, U: Clone>(first: &[T], second: &[U]) -> Vec<(T, U)>
zip()take_first<T: Clone>(items: &[T], n: usize) -> Vec<T> - Return the first n elements using take()skip_first<T: Clone>(items: &[T], n: usize) -> Vec<T> - Skip the first n elements using skip()reverse_sequence<T: Clone>(items: &[T]) -> Vec<T> - Reverse a sequence using rev()interleave<T: Clone>(first: &[T], second: &[T]) -> Vec<T>
sliding_pairs<T: Clone>(items: &[T]) -> Vec<(T, T)>
zip()
with a skipped version// chain_sequences
assert_eq!(
chain_sequences(&[1, 2], &[3, 4]),
vec![1, 2, 3, 4]
);
// zip_pairs
assert_eq!(
zip_pairs(&[1, 2, 3], &["a", "b"]),
vec![(1, "a"), (2, "b")]
);
// take_first
assert_eq!(take_first(&[1, 2, 3, 4, 5], 3), vec![1, 2, 3]);
// skip_first
assert_eq!(skip_first(&[1, 2, 3, 4, 5], 2), vec![3, 4, 5]);
// reverse_sequence
assert_eq!(reverse_sequence(&[1, 2, 3]), vec![3, 2, 1]);
// interleave - alternates elements from both slices
assert_eq!(
interleave(&[1, 3, 5], &[2, 4, 6]),
vec![1, 2, 3, 4, 5, 6]
);
// sliding_pairs - pairs consecutive elements
assert_eq!(
sliding_pairs(&[1, 2, 3, 4]),
vec![(1, 2), (2, 3), (3, 4)]
);chain_sequences, use .iter().chain(other.iter()) and then .cloned().collect()zip_pairs, use .iter().zip(other.iter()) - remember zip stops at the shorter iteratorinterleave, use zip() with flat_map() to flatten tuples into individual elementssliding_pairs, zip the original iterator with a version that skips the first element.iter() on the slice.cloned() to convert &T to T when you need owned values