Reading — step 1 of 7
Learn
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 plaini32
In this lesson's exercise you parse strings into fresh i32 values, so after .map(|s| s.parse::<i32>().unwrap()) the items are owned i32 — filter'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 ownedT
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_whitespace → parse → filter 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 is55, not20. - Summing the evens instead of their squares —
2 + 4 = 6. Themap(|n| n * n)must stay. - Dropping the type annotation on
total— a baresum()iserror[E0282]: type annotations needed. - The hidden test feeds
1 3 5(no evens) and expects0— an empty iterator sums to0, 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…