> Most iterators provide the ability to walk an entire sequence [...] Calling the iterator again walks the sequence again.
> "Single-use iterators" break that convention, providing the ability to walk a sequence only once.
This seems similar to the difference between an "iterable" and an "iterator" in Python (and between `IEnumerable` and `IEnumerator` in C#).
A Python list `l = [1, 2, 3]` is an iterable, and you can do `for v in l: print(v)` multiple times. But `iter(l)` is an iterator and `for v in i: print(v)` will only work once.
It's more between repeatable iterables and one-shot iterables (of which iterators are a subset, as iterators are iterable). For instance a file is an iterable, but (unless it's seekable and you rewind it) it'll only let you iterate once.
Basically what Go calls an iterator most other language call an iterable. Because it uses internal iteration, Go doesn't hand out iterators (save through iter.Pull).
This is the same in JavaScript. There is “pure” Iterable protocol which produces an “impure” Iterator. Interestingly , for loops in JavaScript do not work directly with Iterators; to use in a for loop you must wrap an Iterator in an Iterable
Java is the same if I remember correctly, always felt like a design failure to me.
It's especially dumb for Java, because Iterator could just have been a subtype of Iterable. That's basically what Python does, the iterator protocol requires also being an iterable[0]. And Rust just blanket implements IntoIterator for every Iterator without asking.
At least it's pretty easy to wrap an iterator in JS:
{[Symbol.iterator]: () => it}
[0] which in the rare case you implement an iterator entirely by hand will be a trivial `return self`, so much so that `collection.abc.Iterator` just provides that) Be careful there are bugs and design flaws in Go iterators: https://go101.org/blog/2025-03-15-some-facts-about-iterators...
Your post does show that iterators are somewhat of a leaky abstraction, but I'm not sure I would go as far as calling some of their infelicities "bugs". Whether those infelicities matter in practice is a moot point.
The bug is confirmed: https://github.com/golang/go/issues/71685
- My initial thought is that the performance implication (e.g. escaping to the heap) of impure iterators is an implementation detail and seems like a good candidate for a future compiler optimization.
- impure iterators are strictly more powerful than pure iterators in that they can be resumed but do not have to be. If I wanted to pick one pattern for consistent behavior across all iterators it would be this one.
Not all impure iterators can be resumed. But any pure iterator can be converted to a resumable iterator with a generic conversion function.
> any pure iterator can be converted to a resumable iterator with a generic conversion function.
Makes sense
> Not all impure iterators can be resumed
How do you mean? In my head resuming an iterator is just partially unrolling a loop.
Are the wordings "pure" vs "impure" suitable here, or should it have been "stateless" vs "stateful"?
"Stateless" does not seem as strong as "pure", and purity is what I'm after. What about an iterator that maintains no state but prints to the screen? You could describe it as a "stateless", but not as "pure".