Thuta Learning
Rust
AdvancedProgrammingbeginner

Closures and Iterators

What you'll walk away with

  • Explain the core ideas behind Closures and Iterators
  • Run the sample Rust code and verify its output
  • Apply the technique correctly to the Tutorial Platform and production scenarios

Build the mental model

A closure is an anonymous function whose distinguishing feature is that it can "capture" variables from the scope it was defined in — a closure like `|word| word.len() > min_length` can reference `min_length` from the enclosing scope. There are three ways a closure can capture a variable: by borrow (`&T`, the default, read-only access), by mutable borrow (`&mut T`), or by move (taking ownership, explicit via the `move` keyword) — the compiler automatically decides which, following exactly the ownership rules from Lessons 6 and 7. An iterator is a lazy sequence that traverses a collection element by element, and adapter methods like `map` (transform), `filter` (select), and `collect` (materialize into a final collection) chain together lazily — no actual computation runs until `collect()` is called — so a chain like `iter.filter(...).map(...).collect()` never needs an intermediate collection to hold the filter's results. This iterator chain looks superficially like Java's Stream API or JavaScript's array method chains, but thanks to monomorphization (Lesson 13), Rust's version performs exactly as fast as a hand-written `for` loop — a truly zero-cost abstraction, readable without paying any extra runtime cost, what the Rust community calls "you don't pay for what you don't use."

Connect it to a real scenario

Write the content analyzer's "find lessons above a minimum word count" logic with a closure: `lessons.iter().filter(|l| l.word_count > min_words).collect()` — the closure borrows `min_words` from the enclosing scope. To uppercase every lesson title, chain `lessons.iter().map(|l| l.title.to_uppercase()).collect::<Vec<String>>()` — more readable than a `for` loop, with no performance cost thanks to monomorphization. To find the total word count, write a readable one-liner: `lessons.iter().map(|l| l.word_count).sum::<u32>()` — no intermediate `Vec` needs building, since lazy evaluation means the whole chain only actually traverses once, when `sum()` is called.

Try the working example

rust
struct Lesson { title: String, word_count: u32 }

fn main() {
    let lessons = vec![
        Lesson { title: String::from("Ownership"), word_count: 620 },
        Lesson { title: String::from("Closures"), word_count: 340 },
        Lesson { title: String::from("Traits"), word_count: 700 },
    ];

    let min_words = 500;
    let long_titles: Vec<String> = lessons
        .iter()
        .filter(|l| l.word_count > min_words)
        .map(|l| l.title.to_uppercase())
        .collect();

    println!("{long_titles:?}");

    let total: u32 = lessons.iter().map(|l| l.word_count).sum();
    println!("total words: {total}");
}
You should see
You print `["OWNERSHIP", "TRAITS"]` and "total words: 1660".

5-minute try-it

Create a `Vec<u32>` of integers — use `filter` to keep only even numbers, `map` to double them, and `collect()` into a new `Vec<u32>` — have one of the closures capture a threshold variable.

One important caution

Adding the `move` keyword to a closure unnecessarily, causing it to take ownership of an outer-scope variable — that variable can no longer be used in the outer scope afterward, causing a compile error; a plain borrow (the default) is often enough.

Assuming an iterator chain has run just from writing `.filter(...)` without calling `.collect()` — nothing executes yet because iterators are lazy; the compiler usually flags this with an "unused iterator" warning.

The Rust Programming Language — Processing a Series of Items with IteratorsRust

Easy traps

  • Adding the `move` keyword to a closure unnecessarily, causing it to take ownership of an outer-scope variable — that variable can no longer be used in the outer scope afterward, causing a compile error; a plain borrow (the default) is often enough.
  • Assuming an iterator chain has run just from writing `.filter(...)` without calling `.collect()` — nothing executes yet because iterators are lazy; the compiler usually flags this with an "unused iterator" warning.
  • Validate sample code in a local or test environment before applying it to a production system.

Exercise

Create a `Vec<u32>` of integers — use `filter` to keep only even numbers, `map` to double them, and `collect()` into a new `Vec<u32>` — have one of the closures capture a threshold variable.

You'll know it worked when: You print `["OWNERSHIP", "TRAITS"]` and "total words: 1660".

Closures and Iterators | Thuta Learning