Enumerator への追加は deep copy されるのか(Ruby)

どうもそうなのではないかと思って確かめてみたら、やはりそうだった。これは助かる。

$ pry
[1] pry(main)> e = Enumerator.new do |y|
[1] pry(main)*   a = ["aiueo", "Ruby"]
[1] pry(main)*   y << a
[1] pry(main)*   a[0] = "oeuia"
[1] pry(main)*   y << a
[1] pry(main)*   a[0][1] = "nya"
[1] pry(main)*   y << a
[1] pry(main)* end  
=> #<Enumerator: ...>
[2] pry(main)> loop {p e.next}
["aiueo", "Ruby"]
["oeuia", "Ruby"]
["onyauia", "Ruby"]
=> #<Enumerator::Yielder:0x0056471aa90738>

つまりオブジェクトの完全なスナップショットを取れるということ。

いや、ちがうな。

[3] pry(main)> e.rewind
=> #<Enumerator: ...>
[4] pry(main)> loop {p e.next.object_id}
47431695117340
47431695117340
47431695117340
=> #<Enumerator::Yielder:0x0056471aad58b0>

object_id はすべて同じだ。そうか、Enumerator は Fiber を使って実装されているのだから、y << a した時点で止まってしまうのだな。で、e.next されるたびに更新されると。だから deep copy ではない。実際、to_a してみればわかる。

[5] pry(main)> e.rewind
=> #<Enumerator: ...>
[6] pry(main)> e.to_a
=> [["onyauia", "Ruby"], ["onyauia", "Ruby"], ["onyauia", "Ruby"]]