You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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后,执行顺序完全被打乱:

  1. 初始化阶段调用@enum.next,会直接进入each_plus_one的loop,执行n = @yielder.yield——此时还没有通过feed传入任何值,n被赋值为nil。
  2. 后续执行yield n + 1时,nil没有+方法,直接触发报错。

核心区别在于:

  • Enumerator.each是被动等待型:它会让枚举器先进入等待状态,直到有feed数据传入后,才会触发迭代逻辑并传递有效数据。
  • Enumerator.next是主动拉取型:它会立即尝试获取枚举器的下一个值,此时如果还没有feed数据,就会拿到nil,直接破坏依赖feed的协程逻辑。

内容的提问来源于stack exchange,提问作者Tavian Barnes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 14:51:00