Reading — step 1 of 5
Learn
Iterators
Every loop you've written in this course walks a sequence, filters or transforms it, and collapses it into an answer. Rust's iterators make those three steps vocabulary — and unlike most languages' versions, they compile down to the same machine code as your hand-written loop. Zero-cost abstraction is Rust's brand; iterators are exhibit A.
The pipeline shape
let nums = vec![1, 2, 3, 4, 5, 6];
let total: i64 = nums.iter() // 1. make an iterator
.filter(|n| *n % 2 == 0) // 2. keep evens (adapter)
.map(|n| n * n) // 3. square them (adapter)
.sum(); // 4. collapse (consumer)
// 4 + 16 + 36 = 56
Those |n| n * n things are closures — anonymous inline functions; parameters between pipes, body after. Adapters (filter, map, take, skip, rev, enumerate, zip…) each return a new iterator and do no work yet — iterators are lazy. Only a consumer makes the pipeline run:
.sum() // add everything
.count() // how many survived
.max() / .min() // Option<…> — empty input is a real case!
.collect::<Vec<_>>() // gather into a collection
.any(|n| n > 100) // bool: does one match?
.position(|n| n == 42) // Option<usize>: where?
Forget the consumer and the compiler warns: "iterators are lazy and do nothing unless consumed" — a warning that has saved everyone at least once.
Reading the pipeline vs reading the loop
The equivalent manual version:
let mut total = 0;
for n in &nums {
if n % 2 == 0 {
total += n * n;
}
}
Same result, same performance — genuinely, to the instruction. The difference is what the reader must do: the loop makes them simulate mutable state to learn intent; the pipeline states intent directly — filter evens, square, sum. For a three-line loop it's a taste call. As logic grows, pipelines stay linear while loops grow nested ifs and flag variables — which is why idiomatic Rust leans pipeline, and why clippy (Rust's linter) will actively suggest them.
One mechanical note while it's fresh: .iter() borrows (elements arrive as references — hence *n to compare values), .into_iter() consumes the collection, and ranges are already iterators — (1..=100).sum() needs no vector at all. When the closure's types fight you, that * or a .copied() adapter is usually the answer; the ownership chapter turns those guesses into understanding.
Your exercise: Sum of Squares of Evens
The pipeline above is the exercise — read the numbers, then:
let answer: i64 = nums.iter()
.filter(|n| *n % 2 == 0)
.map(|n| n * n)
.sum();
Deliberate practice: write it both ways — manual loop first, pipeline second — and check they agree on the sample input. That A/B is the fastest route to reading real Rust, where every codebase you'll ever touch is written in the second style. Watch the edge case both versions must share: no evens in the input means the answer is 0, which sum() gives you for free (an empty sum is zero — one more reason it's the right consumer here).
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…