Feature #16816
openPrematurely terminated Enumerator should stay terminated
Description
When iterating over an Enumerator, there are three different possible results of calling next
:
- The next item is returned and a cursor is advanced
- There's no next item and the Enumerator will forever raise
StopIteration
- There's an error getting the next item which is raised out of
next
This third case has some unexpected behavior that I discovered while working on https://github.com/jruby/jruby/issues/6157
It seems that when an Enumerator fails prematurely with an exception, any subsequent call to #next will cause it to restart.
This can be seen in a simple script I used to write a ruby/spec in https://github.com/jruby/jruby/pull/6190
Enumerator.new {
2.times {|i| raise i.to_s }
}.tap {|f|
p 2.times.map { f.next rescue $!.message }
}
The output from this is [0, 0]
. After the iteration fails, the second next
call causes it to restart and it fails again.
Contrast this to the behavior of item 3 above; when an Enumerator finishes iterating without error, it remains "finished" forever and can't be restarted.
I believe the restarting behavior is at best undocumented behavior and at worst incorrect and unspected. Take this example:
e = Enumerator.new { |y|
c = new_database_cursor
5.times { y.yield c.next_result }
}
If next_result
here raises an error, a subsequent call to next
on this enumerator will cause it to restart, re-acquire the cursor, and begin again.
As another example I ask a question: how do you indicate that an Enumerator failed due to an error, and keep it failed so it doesn't restart again?