195. Chain Iterator Adapters — Don't collect() Between Every Step
Every collect::<Vec<_>>() in the middle of a pipeline is a heap allocation and a full pass over your data. Adapters like map and filter are lazy and fuse together — chain them and the whole transformation runs in one pass with zero temporary Vecs.
A Vec between every step
It’s tempting to do one transformation at a time, binding each result to a variable. Every collect() allocates a throwaway Vec and walks the entire sequence before the next step even starts:
| |
Two intermediate Vecs, two extra allocations, three separate passes — all to compute a single number.
One chain, one pass, no temporaries
The adapters compose directly. Nothing is materialized until the final consumer (sum) pulls values through, so there are no intermediate collections at all:
| |
Each element flows through map then filter then into the sum, one at a time. No buffer is ever allocated.
Laziness means short-circuiting works
Because nothing runs until pulled, a chain only does the work it needs. Add a take(2) and the pipeline stops after producing two results — the elements past that point are never touched:
| |
The intermediate-collect version can’t do this: collect() always drains the whole iterator, so it would have visited all eight elements before take ever saw one.
When you genuinely do need a Vec
The point isn’t “never collect” — it’s “don’t collect between steps.” Collect once, at the end, when you actually need an owned, reusable collection:
| |
One collect, at the end, when the Vec is the actual result. Everything before it stays lazy and allocation-free.