What an iterator actually is
An `Iterator` is anything that implements `fn next(&mut self) -> Option<Self::Item>`. That's it. Every adapter — `map`, `filter`, `take`, `skip`, `zip`, `chain` — wraps an existing iterator and returns a new one whose `next()` pulls from the inner one. Because each step is just a function call, the optimiser inlines an entire chain into a single tight loop. This is what "zero-cost abstraction" means in practice: an iterator chain produces the same machine code as the equivalent hand-rolled `for` loop.
Adapter taxonomy
There are roughly five categories of iterator adapter. Transformers (`map`, `inspect`, `enumerate`) reshape items one-to-one. Filters (`filter`, `take_while`, `skip_while`) drop items conditionally. Combiners (`zip`, `chain`, `cycle`, `interleave`) splice multiple iterators together. Slicers (`take`, `skip`, `step_by`, `chunks`) bound or window the stream. Consumers (`sum`, `count`, `fold`, `collect`, `for_each`) drive the iterator and return a final value. Only consumers actually do work — every other adapter is lazy.
For deeper background, see a complete cheat-sheet of std collection complexities for the broader context behind this section.
collect, the polymorphic finaliser
`collect` builds any type that implements `FromIterator`. The destination is chosen by the type annotation: `let v: Vec<_> = iter.collect();` builds a Vec, `let s: HashSet<_> = ...` builds a HashSet, `let m: HashMap<_, _> = ...` builds a map from `(K, V)` pairs. You can implement `FromIterator` for your own types to plug them into the same machinery.
Custom iterators
Implementing `Iterator` for your own type takes one method: `next`. The compiler will then derive every adapter for free. For finite-length iterators you should also implement `ExactSizeIterator`, and for ones that can be walked from both ends `DoubleEndedIterator`. These extra impls let collect pre-allocate exactly the right capacity and let `rev()` work without buffering.
For deeper background, see the official Rust API guidelines for module-level design for the broader context behind this section.
Common pitfalls
An iterator that is built but never consumed does nothing — `things.iter().map(|x| println!(...))` is a no-op because `map` is lazy. Iterators borrow from their source — you cannot mutate the source while a non-consumed iterator is in scope. And `collect::<Vec<_>>()` allocates; if you only need to traverse, prefer `for_each` or a plain `for` loop.