Ruby中外部迭代失效但内部迭代正常:Enumerator.feed异常原因解析
为什么用
Enumerator.next替代each迭代会触发nil错误? 正常运行的代码
class Foo def initialize @enum = Enumerator.new do |yielder| @yielder = yielder plus_ones = enum_for(:each_plus_one) plus_ones.each do |n| puts n end end @enum.next end def <<(n) @enum.feed(n) @enum.next end def each_plus_one loop do n = @yielder.yield yield n + 1 end end end foo = Foo.new foo << 1 foo << 2 foo << 3
执行结果:
$ ruby foo.rb 2 3 4
触发错误的代码(仅修改initialize方法)
class Foo def initialize @enum = Enumerator.new do |yielder| @yielder = yielder plus_ones = enum_for(:each_plus_one) loop do puts plus_ones.next rescue StopIteration break end end @enum.next end # << 和 each_plus_one 方法与正常代码一致 end
报错信息:
$ ruby foo.rb nil foo.rb:27:in 'block in Foo#each_plus_one': undefined method '+' for nil (NoMethodError) yield n + 1 ^ from <internal:kernel>:168:in 'Kernel#loop' from foo.rb:25:in 'Foo#each_plus_one' from foo.rb:in 'Enumerator#each'
问题根源:Enumerator.each与Enumerator.next的执行逻辑差异
当使用plus_ones.each迭代时,Ruby枚举器会自动维护迭代的上下文关联:每次@yielder.yield被调用时,feed传入的值会被正确传递给each的块参数n,确保只有拿到有效数据后才会执行后续逻辑。
但换成plus_ones.next后,执行顺序完全被打乱:
- 初始化阶段调用
@enum.next,会直接进入each_plus_one的loop,执行n = @yielder.yield——此时还没有通过feed传入任何值,n被赋值为nil。 - 后续执行
yield n + 1时,nil没有+方法,直接触发报错。
核心区别在于:
Enumerator.each是被动等待型:它会让枚举器先进入等待状态,直到有feed数据传入后,才会触发迭代逻辑并传递有效数据。Enumerator.next是主动拉取型:它会立即尝试获取枚举器的下一个值,此时如果还没有feed数据,就会拿到nil,直接破坏依赖feed的协程逻辑。
内容的提问来源于stack exchange,提问作者Tavian Barnes
相关产品推荐
相关产品推荐

