Skip to content
Iterator Adaptors
step 1/7

Reading — step 1 of 7

Learn

~3 min readIterators, Result, Option

Iterator Adaptors

Rust iterators are lazy and zero-cost: a chain of adaptors compiles into the same tight loop you'd write by hand — no allocations, no callbacks at runtime.

Lazy means nothing happens until you consume

let nums = vec![1, 2, 3, 4, 5];
nums.iter().map(|n| n * 10);     // does NOTHING
warning: unused `std::iter::Map` that must be used
note: iterators are lazy and do nothing unless consumed

Adaptors (map, filter, ...) only build a description of the computation. A terminal operation (sum, collect, count, a for loop) actually runs it.

The chain this lesson is about

let nums = vec![1, 2, 3, 4, 5];

let sum: i32 = nums.iter()           // yields &i32
    .filter(|&&n| n % 2 == 0)        // keep the evens: 2, 4
    .map(|&n| n * n)                 // square them: 4, 16
    .sum();                          // 20

Adaptors (return a new iterator): map, filter, filter_map (transform and drop the Nones), flat_map, take(n), skip(n), zip, enumerate, chain, rev.

Terminal ops (consume the iterator): collect, sum, product, count, fold(init, f), find, any, all, max, min.

The reference layers — the classic stumbling block

.iter() borrows: it yields &i32, not i32. And filter hands its closure a reference to the item, so inside filter you're looking at &&i32:

nums.iter().filter(|n| n % 2 == 0)   // n: &&i32
error[E0369]: binary operation `%` cannot be applied to type `&&i32`

Fixes — pick one:

  • pattern-match the references away: .filter(|&&n| ...), .map(|&n| ...)
  • dereference explicitly: .filter(|n| **n % 2 == 0)
  • convert to owned values early: .iter().copied() yields plain i32

In this lesson's exercise you parse strings into fresh i32 values, so after .map(|s| s.parse::<i32>().unwrap()) the items are owned i32filter's closure then sees a plain &i32, and n % 2 == 0 just works. (The graded input is always valid integers, so unwrap() is fine here; the next lesson covers what to do when it isn't.)

sum() needs to know its type

let total = nums.iter().sum();        // error[E0282]: type annotations needed
let total: i32 = nums.iter().sum();   // idiomatic fix
let total = nums.iter().sum::<i32>(); // turbofish also works

Same for collect: let v: Vec<i32> = it.collect(); or it.collect::<Vec<i32>>().

iter vs iter_mut vs into_iter

  • .iter() — borrow each element (&T); the collection is still usable after
  • .iter_mut() — mutable borrow (&mut T); modify elements in place
  • .into_iter() — consume the collection, yielding owned T

for x in &v is sugar for iterating over &Vec<T>, which yields &T.

Your exercise

Read one line of space-separated integers; print the sum of the squares of the even ones. The commented chain in the starter is the whole solution: split_whitespaceparsefilter even → map square → sum, then println!("{}", total). (split_whitespace also swallows the trailing newline — no trim needed.)

TEST 1 feeds 1 2 3 4 5 and expects exactly:

20

Mistakes the grader catches:

  • Forgetting the filter — the sum of ALL the squares is 55, not 20.
  • Summing the evens instead of their squares2 + 4 = 6. The map(|n| n * n) must stay.
  • Dropping the type annotation on total — a bare sum() is error[E0282]: type annotations needed.
  • The hidden test feeds 1 3 5 (no evens) and expects 0 — an empty iterator sums to 0, so the correct chain passes with no special-casing. Don't add any.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…