🔥
[Bug #20444] loop ないで StopIteration 例外を発生させたときに loop が意図する値を返さないバグ報告
[Bug #2044] Kernel#loop: returning the "result" value of StopIteration doesn't work when raised directly
-
Enumerator#next
でイテレーションするときに『最後の要素』で#next
を呼び出すとStopIteration
の例外が発生する - またこのときに
StopIteration#result
で『例外を発生させる原因となったメソッドなどの戻り値』を受け取る事ができる- 下記の例だと
Enumerator.new
のブロックの戻り値を返す - StopIteration#result (Ruby 3.3 リファレンスマニュアル)
- 下記の例だと
enum = Enumerator.new { |y|
y << 1
y << 2
:ok
}
begin
pp enum.next # => 1
pp enum.next # => 2
# 次の要素がないのでここで例外が発生する
enum.next
rescue StopIteration => e
# StopIteration#result は Enumerator.new のブロック引数の戻りを返す
pp "error: #{e.result}"
# => "error: ok"
end
- この
StopIteration
例外なんですが、実はKernel.#loop
でも参照れされており『#loop
内でStopIteration
例外が発生したら loop から抜けてStopIteration#result
の値を返す』という挙動になっている
enum = Enumerator.new { |y|
y << 1
y << 2
:ok
}
result = loop {
# 最後の要素で #next を呼び出すと StopIteration が発生し loop が終了する
# loop の戻り値は StopIteration#result の値を返す
pp enum.next
}
pp result
# => :ok
- で、ここまでが前ふりなんですがこのチケットでは次のように『意図的に
StopIteration
を発生させた場合にloop
で意図する値が返ってこない』というバグ報告になる
# この場合 3 を返すことを期待するが nil が返ってくる
result = loop {
raise StopIteration.new(3)
}
pp result
# => nil
- これなんですが
StopIteration.new(3)
の#result
の戻り値がnil
になるので期待する挙動になる
e = StopIteration.new(3)
# message には含まれるが #result は nil になる
pp e.message # => "3"
pp e.result # => nil
- また Ruby レイヤーでは
StopIteration#result
を設定する事はできないとのこと -
loop
ほとんど使ったことがないのでこういう挙動があるのは知らなかった
Discussion