Syntax Cache
BlogMethodFeaturesHow It WorksBuild a Game
  1. Home
  2. Cheat Sheets
  3. Rust
  4. Rust Iterators Cheat Sheet
RustCheat Sheet

Rust Iterators Cheat Sheet

Quick-reference for iterator adapters, consumers, and ownership. Each section includes copy-ready snippets with inline output comments.

On this page
  1. 1iter() vs into_iter() vs iter_mut()
  2. 2map() and filter()
  3. 3fold() and reduce()
  4. 4collect()
  5. 5zip() and enumerate()
  6. 6take(), skip(), chain()
  7. 7any(), all(), find(), position()
  8. 8flat_map() and flatten()
  9. 9copied() and cloned()
  10. 10Custom Iterators
  11. 11Lazy Evaluation
  12. 12Ranges as Iterators
iter() vs into_iter() vs iter_mut()map() and filter()fold() and reduce()collect()zip() and enumerate()take(), skip(), chain()any(), all(), find(), position()flat_map() and flatten()copied() and cloned()Custom IteratorsLazy EvaluationRanges as Iterators

iter() vs into_iter() vs iter_mut()

The ownership decision: borrow, consume, or mutate in place.

iter(): borrows elements (&T)
let v = vec![1, 2, 3];
for x in v.iter() {
    println!("{x}");    // x is &i32
}
println!("{v:?}");      // v still usable
into_iter(): takes ownership (T)
let v = vec![1, 2, 3];
for x in v.into_iter() {
    println!("{x}");    // x is i32 (owned)
}
// println!("{v:?}");   // ERROR: v was consumed
iter_mut(): mutable borrows (&mut T)
let mut v = vec![1, 2, 3];
for x in v.iter_mut() {
    *x *= 2;            // modify in place
}
println!("{v:?}");      // => [2, 4, 6]
for loop sugar
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.

map: transform each element
let doubled: Vec<i32> = vec![1, 2, 3]
    .iter()
    .map(|&x| x * 2)
    .collect();
// => [2, 4, 6]
filter: keep matching elements
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.

filter_map: transform and filter in one step
let numbers: Vec<i32> = vec!["1", "two", "3"]
    .iter()
    .filter_map(|s| s.parse::<i32>().ok())
    .collect();
// => [1, 3]
Chaining: filter then map
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.

fold: accumulate with initial value
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);
// => 24
fold into a different type
let 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"
reduce: no initial value
let max = vec![3, 1, 4, 1, 5].into_iter().reduce(i32::max);
// => Some(5)  -- returns Option because vec might be empty
sum and product shortcuts
let sum: i32 = vec![1, 2, 3].iter().sum();       // => 6
let prod: i32 = vec![1, 2, 3].iter().product();  // => 6

collect()

Turn an iterator into a collection. Must annotate the target type because collect can build many types.

Collect into Vec
let v: Vec<i32> = (1..=5).collect();
// or with turbofish:
let v = (1..=5).collect::<Vec<i32>>();
Collect into HashMap
use std::collections::HashMap;

let map: HashMap<&str, i32> = vec![("a", 1), ("b", 2)]
    .into_iter()
    .collect();
Collect into String
let s: String = vec!['h', 'e', 'l', 'l', 'o']
    .into_iter()
    .collect();
// => "hello"
Collect Result<Vec<T>, E>
let results: Result<Vec<i32>, _> = vec!["1", "2", "3"]
    .iter()
    .map(|s| s.parse::<i32>())
    .collect();
// => Ok([1, 2, 3])  -- short-circuits on first Err

collect::<Result<Vec<_>, _>>() stops at the first Err and returns it.

zip() and enumerate()

Pair elements from two iterators with zip. Add indices with enumerate.

enumerate: (index, element)
let names = vec!["Alice", "Bob", "Carol"];
for (i, name) in names.iter().enumerate() {
    println!("{i}: {name}");
}
// => 0: Alice, 1: Bob, 2: Carol
zip: pair two iterators
let 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.

zip into HashMap
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.

take: first N elements
let first_three: Vec<i32> = (1..=10).take(3).collect();
// => [1, 2, 3]
skip: drop first N elements
let rest: Vec<i32> = (1..=5).skip(2).collect();
// => [3, 4, 5]
take_while and skip_while
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]
chain: concatenate iterators
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.

any: does any element match?
let has_even = vec![1, 3, 4, 7].iter().any(|&x| x % 2 == 0);
// => true
all: do all elements match?
let all_positive = vec![1, 2, 3].iter().all(|&x| x > 0);
// => true
find: first matching element
let first_even = vec![1, 3, 4, 7].iter().find(|&&x| x % 2 == 0);
// => Some(&4)
position: index of first match
let idx = vec![10, 20, 30].iter().position(|&x| x == 20);
// => Some(1)
find_map: find and transform
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.

flatten: remove one level of nesting
let nested = vec![vec![1, 2], vec![3, 4]];
let flat: Vec<i32> = nested.into_iter().flatten().collect();
// => [1, 2, 3, 4]
flat_map: map then flatten
let words: Vec<&str> = vec!["hello world", "foo bar"]
    .iter()
    .flat_map(|s| s.split_whitespace())
    .collect();
// => ["hello", "world", "foo", "bar"]
Flatten Options
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).

copied: for Copy types (integers, bools, etc.)
let nums = vec![1, 2, 3];
let owned: Vec<i32> = nums.iter().copied().collect();
// => [1, 2, 3]  -- no allocation, just bitwise copy
cloned: for Clone types (String, Vec, etc.)
let names = vec![String::from("alice"), String::from("bob")];
let owned: Vec<String> = names.iter().cloned().collect();
// => ["alice", "bob"]  -- each String is heap-allocated

Prefer 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.

Counter iterator
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]
All adapters work on custom iterators
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.

Nothing happens without a consumer
let v = vec![1, 2, 3];
v.iter().map(|x| {
    println!("processing {x}");  // NEVER PRINTED
    x * 2
});  // no consumer -- entire chain is a no-op

The compiler warns: "unused Map that must be used". Add .collect(), .sum(), .for_each(), or a for loop.

Consumers that drive the iterator
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.

Exclusive and inclusive ranges
let exclusive: Vec<i32> = (1..5).collect();   // => [1, 2, 3, 4]
let inclusive: Vec<i32> = (1..=5).collect();  // => [1, 2, 3, 4, 5]
Range with adapters
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();
// => 2550
rev: reverse iteration
let countdown: Vec<i32> = (1..=5).rev().collect();
// => [5, 4, 3, 2, 1]
step_by: custom stride
let odds: Vec<i32> = (1..=10).step_by(2).collect();
// => [1, 3, 5, 7, 9]
Learn Rust in Depth
Rust Ownership & Borrowing Practice →Rust Traits & Generics Practice →
Warm-up1 / 2

Can you write this from memory?

Create an empty Vec of i32 values named `numbers`.

See Also
Traits & Generics →Pattern Matching →

Start Practicing Rust

Free daily exercises with spaced repetition. No credit card required.

← Back to Rust Syntax Practice
Syntax Cache

Build syntax muscle memory with spaced repetition.

Product

  • Pricing
  • Our Method
  • Daily Practice
  • Design Patterns
  • Interview Prep

Resources

  • Blog
  • Compare
  • Cheat Sheets
  • Vibe Coding
  • Muscle Memory

Languages

  • Python
  • JavaScript
  • TypeScript
  • Rust
  • SQL
  • GDScript

Legal

  • Terms
  • Privacy
  • Contact

© 2026 Syntax Cache

Cancel anytime in 2 clicks. Keep access until the end of your billing period.

No refunds for partial billing periods.