ProgramGuru

Iterators, closures and transformations

Iterators, closures and transformations

A teacher wants adjusted marks only for students who have met the target. An iterator supplies items one at a time. Adapters such as filter and map describe a transformation, while a consuming operation such as collect actually drives it. A closure is an inline function-like value that can capture surrounding data.

What you’ll learn

  • Trace filter and map through each original mark.
  • Recognize lazy adapters, captured values and consuming iteration.

Iterators, closures and transformations in action

  1. Input: 60, 75, 90

    The pipeline drops 60, keeps 75 and 90, then adds the captured bonus of five.

    Input: 60, 75, 90
  2. Filter: 75, 90

    Collect produces a new vector containing 80 and 95.

    Filter: 75, 90
  3. Map +5: 80, 95

    The source vector remains unchanged because the iteration borrowed it and copied its integer elements.

    Map +5: 80, 95
The pipeline drops 60, keeps 75 and 90, then adds the captured bonus of five. Collect produces a new vector containing 80 and 95. The source vector remains unchanged because the iteration borrowed it and copied its integer elements.

Iterators, closures and transformations explained

iter() borrows elements; copied() turns these Copy integer references into integer values. Filter keeps values whose predicate is true, map transforms each remaining value, and collect builds the result. Most iterator adapters are lazy: describing the pipeline does not itself perform every operation.

Work through the Rust program

Save the complete source as main.rs in the Rust environment. No standard input is needed.

Rust 2024 · complete program

fn main() {
  let marks = vec![60, 75, 90];
  let bonus = 5;
  let adjusted: Vec<_> = marks.iter().copied()
    .filter(|score| *score >= 75)
    .map(|score| score + bonus)
    .collect();
  println!("Adjusted: {adjusted:?}");
  println!("Original: {marks:?}");
}

Expected output

Adjusted: [80, 95]
Original: [60, 75, 90]

Follow the execution

The pipeline drops 60, keeps 75 and 90, then adds the captured bonus of five. Collect produces a new vector containing 80 and 95. The source vector remains unchanged because the iteration borrowed it and copied its integer elements.

Try the complete program in the Rust compiler. For tests, Cargo commands and local files, use the specified local environment.

Choosing how a closure captures data

A closure that only reads the bonus can borrow it. A closure that changes captured state needs mutable calling access, and one that consumes a captured value can be callable only once. move changes capture to ownership or copying; it does not mean the closure must run on another thread. A closure’s Fn, FnMut and FnOnce capabilities follow what its body does with captures.

For a simple total, a loop may be easier for a beginner to debug. Use a pipeline when its sequence of filtering and transformation communicates the policy clearly, not because it is automatically faster.

Try a small variation

Your task: What result would a bonus of zero produce?

Rust 2024 · complete program

fn main() {
  let marks = vec![60, 75, 90];
  let bonus = 5;
  let adjusted: Vec<_> = marks.iter().copied()
    .filter(|score| *score >= 75)
    .map(|score| score + bonus)
    .collect();
  println!("Adjusted: {adjusted:?}");
  println!("Original: {marks:?}");
}

Worked answer

It would produce [75, 90]. Filtering still removes 60; changing the mapping bonus does not remove the filter operation.

Mistakes and limits to recognize

Closures can capture by shared borrow, mutable borrow or ownership. Fn, FnMut and FnOnce describe calling requirements; they do not mean every closure has the same captures. A pipeline may consume its source when built from into_iter rather than iter.

Check your understanding

What result would a bonus of zero produce?
Choose one answer. You can try again.

Answer and explanation

It would produce [75, 90].

It would produce [75, 90]. Filtering still removes 60; changing the mapping bonus does not remove the filter operation.

Take the idea into your next program

It would produce [75, 90]. Filtering still removes 60; changing the mapping bonus does not remove the filter operation. Closures can capture by shared borrow, mutable borrow or ownership.

Further reading: the official Rust Book on iterators, closures and transformations.

Reaching the recap and completing the quizzes are separate milestones.

    Next lesson: Structs and named fields →