106. Iterator::by_ref — Take Part of an Iterator and Keep the Rest
Called iter.take(n).collect() and watched the borrow checker swallow the whole iterator? by_ref lets you consume a chunk and keep iterating from where you stopped.
The trap: adapters consume their iterator
Most iterator adapters take self, not &mut self. The moment you write iter.take(2), you have moved iter into the new Take adapter. The original binding is gone:
| |
So even though there are clearly elements left, the variable that points at them is no longer usable.
The fix: iter.by_ref()
Iterator::by_ref returns a &mut Self — and &mut I itself implements Iterator whenever I: Iterator. Adapters chained off by_ref() consume from the underlying iterator without moving it:
| |
Same iterator, two phases, no clones, no indexing.
A real use: parse a header, then a body
The pattern shines when the front of a stream uses different rules than the rest. Pull lines until you hit a blank one, then hand the same iterator to the body parser:
| |
Without by_ref, take_while would have consumed lines whole and the body would be unreachable.
Gotcha: take_while peeks one past
take_while reads the first non-matching element to know when to stop — and that element is gone from the underlying iterator. With by_ref you’ll see this directly:
| |
If you need to keep the boundary element, reach for Peekable instead.
When to use it
Any time you want to apply an adapter to part of an iterator and continue afterwards: chunked parsing, header/body splits, “take the first N then process the rest”, consuming until a sentinel. by_ref() turns “I moved my iterator and now I can’t use it” into a single extra method call.