We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Closures and Iterators step 15 of 28
scan: a running balance that stops at the floor
Track a running balance and stop the moment it would go under a floor.
pub fn balances(deltas: Vec<i64>, floor: i64) -> Vec<i64>
The balance starts at 0. Apply each delta in turn and emit the balance
after that delta. The moment applying a delta would take the balance
below floor, stop: that balance is not emitted, and neither is
anything after it. This is a clean stop, not an error.
deltas = [10, -3, 5], floor = 0 -> [10, 7, 12]
deltas = [10, -20, 5], floor = 0 -> [10] (-10 would breach)
deltas = [-1], floor = 0 -> []
deltas = [], floor = 0 -> []
Equal to the floor is fine — only strictly below stops it.
scan is fold that yields as it goes
fn scan<St, B, F>(self, initial_state: St, f: F) -> Scan<Self, St, F>
where F: FnMut(&mut St, Self::Item) -> Option<B>;
Read the closure’s type carefully, because both halves are the lesson.
It receives &mut St. Not St, not St returned back. The adapter owns
the state and lends it to you mutably on every call. So every touch of the
state goes through a dereference:
let next = *state + d; // read
*state = next; // write
Forget a * and you get a diagnostic about &mut i64 where an i64 was
expected — the starter has both mistakes, and the first one reads
error[E0369]: cannot add `i64` to `&mut i64`
because Add is implemented for &i64 but not for &mut i64. This is the
same family of confusion as item 9.7’s &&i64: the reference level is part
of the type, and operators do not paper over it.
It returns Option<B>, and None ends the iterator. That is what makes
scan the right tool here rather than a hand-rolled loop: “keep going while
this holds, then stop” is already in the adapter’s contract. Some(v) yields
v; None terminates — permanently, like take_while, not like filter.
And note B is whatever you yield, which need not be St. Here they are
both i64 and it is tempting to write Some(state); that yields the
reference, and the error lands at the far end of the chain:
error[E0277]: a value of type `Vec<i64>` cannot be built from an iterator
over elements of type `&mut i64`
A type error inside a closure often does not surface at the closure. It
surfaces at collect, which is the first place the compiler has a concrete
expectation to compare against. When collect complains about an item type
you did not think you produced, walk back up the chain.
Why not fold or try_fold?
Because the answer is the whole prefix, not a single value.
fold returns one accumulator at the end. try_fold returns one
accumulator or a short circuit. Neither can yield the intermediate states,
so making them work here means pushing into a Vec you carry as the
accumulator — which is a fold pretending to be a scan, and loses laziness:
the whole input is walked even though the answer stopped at element two.
scan is lazy. Chain a .take(3) after it and only three states are ever
computed. That composability is the reason the adapter exists.
Why not a for loop with a mutable local?
You can. It is also what clippy’s needless_range_loop and
explicit_counter_loop are usually pushing you away from, and — more
importantly — a loop is a statement, not a value. You cannot pass a loop to
.take(3), you cannot chain another adapter onto it, and you cannot hand it
to a function expecting an iterator. scan gives you the same computation as
a value.
The honest counterpoint: for a one-off computation whose result you consume
immediately, a loop is often clearer, and nobody should reach for scan to
prove a point. Reach for it when the running state is part of a chain.
Detail
scan‘s state is created once, before iteration begins, and lives in the
Scan adapter. It is not re-created per element and it is not shared
with anything else — which is why &mut is safe here without any of item
9.8’s RefCell machinery.
Grade is compile + tests + clippy -D warnings.
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.