Project

General

Profile

Bug #16816

Updated by headius (Charles Nutter) 3 months ago

When iterating over an Enumerator, there are three different possible results of calling `next`: 

 1. The next item is returned and a cursor is advanced 
 2. There's no next item and the Enumerator will forever raise `StopIteration` 
 3. 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 https://github.com/jruby/jruby/issue/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 

 ```ruby 
 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: 

 ```ruby 
 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?

Back