I’ve run into an unexpected (for me) issue recently. Consider the following example where a simple struct
is used as an Iterator
struct MyIter
include Iterator(Int32)
def initialize; @index = 0; end
def next; @index == 5 ? stop : (@index += 1); end
end
the important point is that it’s a struct
and that its current state is represented by a value type @index
. Now, the following code
MyIter.new.map do |i|
MyIter.new.map do |j|
j
end
end.flatten.to_a
enters an infinite loop. The reason is how the flatten
iterator is implemented: it manages the nested iterators in an array, thus copying the inner iterator. When it access the inner iterator to advance it, it does not actually modify that iterator but a copy of it (something like @generators.last.next
, the next
method will be called on a copy, not on the iterator stored in @generators
). Hence, the inner iterator will remain on its first element forever.
Of course, the problem vanishes if the iterator is a class
not a struct
or if its state is not represented by a value type.
In hindsight this is totally expected but it was pretty surprising to me (and took me some time to figure out what’s going on, in particular, because many iterators within the stdlib are implemented as struct
, too).
So the real question is: what should be done about this? Is this considered a bug? Should the implementation of Flatten
be changed? Should it be mentioned in docs (or is it already and I missed the hint?)? Am I the only one who has been surprised by this?