Rust Iterators Cheat Sheet
Quick-reference for iterator adapters, consumers, and ownership. Each section includes copy-ready snippets with inline output comments.
iter() vs into_iter() vs iter_mut()
The ownership decision: borrow, consume, or mutate in place.
let v = vec![1, 2, 3];
for x in v.iter() {
println!("{x}"); // x is &i32
}
println!("{v:?}"); // v still usablelet v = vec![1, 2, 3];
for x in v.into_iter() {
println!("{x}"); // x is i32 (owned)
}
// println!("{v:?}"); // ERROR: v was consumedlet mut v = vec![1, 2, 3];
for x in v.iter_mut() {
*x *= 2; // modify in place
}
println!("{v:?}"); // => [2, 4, 6]let v = vec![1, 2, 3];
for x in &v { } // same as v.iter()
for x in &mut v { } // same as v.iter_mut()
for x in v { } // same as v.into_iter()map() and filter()
Transform elements with map. Select elements with filter. Both are lazy -- nothing happens until consumed.
let doubled: Vec<i32> = vec![1, 2, 3]
.iter()
.map(|&x| x * 2)
.collect();
// => [2, 4, 6]let evens: Vec<&i32> = vec![1, 2, 3, 4, 5]
.iter()
.filter(|&&x| x % 2 == 0)
.collect();
// => [&2, &4]filter receives &&T (double reference) because iter() yields &T and filter borrows that.
let numbers: Vec<i32> = vec!["1", "two", "3"]
.iter()
.filter_map(|s| s.parse::<i32>().ok())
.collect();
// => [1, 3]let result: Vec<i32> = (1..=10)
.filter(|x| x % 3 == 0)
.map(|x| x * x)
.collect();
// => [9, 36, 81]fold() and reduce()
Aggregate elements into a single value. fold takes an initial value; reduce uses the first element.
let sum = vec![1, 2, 3, 4].iter().fold(0, |acc, &x| acc + x);
// => 10
let product = vec![1, 2, 3, 4].iter().fold(1, |acc, &x| acc * x);
// => 24let csv = vec!["a", "b", "c"]
.iter()
.fold(String::new(), |mut acc, &s| {
if !acc.is_empty() { acc.push(','); }
acc.push_str(s);
acc
});
// => "a,b,c"let max = vec![3, 1, 4, 1, 5].into_iter().reduce(i32::max);
// => Some(5) -- returns Option because vec might be emptylet sum: i32 = vec![1, 2, 3].iter().sum(); // => 6
let prod: i32 = vec![1, 2, 3].iter().product(); // => 6collect()
Turn an iterator into a collection. Must annotate the target type because collect can build many types.
let v: Vec<i32> = (1..=5).collect();
// or with turbofish:
let v = (1..=5).collect::<Vec<i32>>();use std::collections::HashMap;
let map: HashMap<&str, i32> = vec![("a", 1), ("b", 2)]
.into_iter()
.collect();let s: String = vec!['h', 'e', 'l', 'l', 'o']
.into_iter()
.collect();
// => "hello"let results: Result<Vec<i32>, _> = vec!["1", "2", "3"]
.iter()
.map(|s| s.parse::<i32>())
.collect();
// => Ok([1, 2, 3]) -- short-circuits on first Errcollect::<Result<Vec<_>, _>>() stops at the first Err and returns it.
zip() and enumerate()
Pair elements from two iterators with zip. Add indices with enumerate.
let names = vec!["Alice", "Bob", "Carol"];
for (i, name) in names.iter().enumerate() {
println!("{i}: {name}");
}
// => 0: Alice, 1: Bob, 2: Carollet keys = vec!["a", "b", "c"];
let vals = vec![1, 2, 3];
let pairs: Vec<_> = keys.iter().zip(vals.iter()).collect();
// => [("a", 1), ("b", 2), ("c", 3)]zip stops at the shorter iterator.
use std::collections::HashMap;
let keys = vec!["name", "age"];
let vals = vec!["Alice", "30"];
let map: HashMap<_, _> = keys.into_iter().zip(vals).collect();take(), skip(), chain()
Slice iterators with take/skip. Concatenate with chain.
let first_three: Vec<i32> = (1..=10).take(3).collect();
// => [1, 2, 3]let rest: Vec<i32> = (1..=5).skip(2).collect();
// => [3, 4, 5]let v: Vec<i32> = vec![1, 2, 3, 4, 5];
let small: Vec<_> = v.iter().take_while(|&&x| x < 4).collect();
// => [&1, &2, &3]
let rest: Vec<_> = v.iter().skip_while(|&&x| x < 4).collect();
// => [&4, &5]let combined: Vec<i32> = vec![1, 2].into_iter()
.chain(vec![3, 4])
.collect();
// => [1, 2, 3, 4]any(), all(), find(), position()
Short-circuiting search and check methods. These consume the iterator.
let has_even = vec![1, 3, 4, 7].iter().any(|&x| x % 2 == 0);
// => truelet all_positive = vec![1, 2, 3].iter().all(|&x| x > 0);
// => truelet first_even = vec![1, 3, 4, 7].iter().find(|&&x| x % 2 == 0);
// => Some(&4)let idx = vec![10, 20, 30].iter().position(|&x| x == 20);
// => Some(1)let parsed = vec!["foo", "42", "bar"]
.iter()
.find_map(|s| s.parse::<i32>().ok());
// => Some(42)flat_map() and flatten()
Flatten nested iterators. flat_map combines map + flatten in one step.
let nested = vec![vec![1, 2], vec![3, 4]];
let flat: Vec<i32> = nested.into_iter().flatten().collect();
// => [1, 2, 3, 4]let words: Vec<&str> = vec!["hello world", "foo bar"]
.iter()
.flat_map(|s| s.split_whitespace())
.collect();
// => ["hello", "world", "foo", "bar"]let opts = vec![Some(1), None, Some(3)];
let values: Vec<i32> = opts.into_iter().flatten().collect();
// => [1, 3]Option implements IntoIterator, so flatten skips None values.
copied() and cloned()
Convert &T to T. Use copied for Copy types (cheap). Use cloned for Clone types (may allocate).
let nums = vec![1, 2, 3];
let owned: Vec<i32> = nums.iter().copied().collect();
// => [1, 2, 3] -- no allocation, just bitwise copylet names = vec![String::from("alice"), String::from("bob")];
let owned: Vec<String> = names.iter().cloned().collect();
// => ["alice", "bob"] -- each String is heap-allocatedPrefer copied when T: Copy. It documents that the operation is cheap.
Custom Iterators
Implement the Iterator trait to create your own iterator. Only next() is required.
struct Counter { count: u32 }
impl Counter {
fn new() -> Self { Counter { count: 0 } }
}
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<u32> {
if self.count < 5 {
self.count += 1;
Some(self.count)
} else {
None
}
}
}
let v: Vec<u32> = Counter::new().collect();
// => [1, 2, 3, 4, 5]let sum: u32 = Counter::new()
.zip(Counter::new().skip(1))
.map(|(a, b)| a * b)
.filter(|&x| x % 3 == 0)
.sum();Implementing Iterator::next gives you map, filter, fold, and 70+ other methods for free.
Lazy Evaluation
Iterator adapters do nothing until consumed. This enables zero-allocation chaining.
let v = vec![1, 2, 3];
v.iter().map(|x| {
println!("processing {x}"); // NEVER PRINTED
x * 2
}); // no consumer -- entire chain is a no-opThe compiler warns: "unused Map that must be used". Add .collect(), .sum(), .for_each(), or a for loop.
let v = vec![1, 2, 3, 4, 5];
// collect: build a collection
let doubled: Vec<_> = v.iter().map(|x| x * 2).collect();
// sum/product: aggregate
let total: i32 = v.iter().sum();
// for_each: side effects
v.iter().for_each(|x| println!("{x}"));
// for loop
for x in &v { println!("{x}"); }Ranges as Iterators
Rust ranges (1..10, 0..=5) implement Iterator. Use them for integer sequences.
let exclusive: Vec<i32> = (1..5).collect(); // => [1, 2, 3, 4]
let inclusive: Vec<i32> = (1..=5).collect(); // => [1, 2, 3, 4, 5]let squares: Vec<i32> = (1..=5).map(|x| x * x).collect();
// => [1, 4, 9, 16, 25]
let even_sum: i32 = (1..=100).filter(|x| x % 2 == 0).sum();
// => 2550let countdown: Vec<i32> = (1..=5).rev().collect();
// => [5, 4, 3, 2, 1]let odds: Vec<i32> = (1..=10).step_by(2).collect();
// => [1, 3, 5, 7, 9]Can you write this from memory?
Create an empty Vec of i32 values named `numbers`.