Just pointing out the obvious:
For the simple iterators/generators that run on a non-changing
source you can basically break it up into:
1. iterators without lookahead
2. iterators with lookahead
Which is basically the same issues you deal with when
implementing a lexer.
Python-style iterators/generators is basically the former. That
comes with one set of advantages, but no lookahead. But lookahead
frequently have cost penalties. There are many tradeoffs. And
those tradeoffs become rather clear when you consider factors
like:
1. mutating iterators
2. the size of the object
3. copyable iterators
4. concurrency/thread safety
5. progress with high computational cost
6. high computational cost for the value
7. sources with latency
8. skip functionality
7. non-inlineable situations
8. exceptions
9. complex iterators (e.g. interpolation)
etc
There are massive tradeoffs even when writing iterators for
really simple data-structures like the linked list. It all
depends on what functionality one are looking for.
There is no best solution. It all depends on the application.