Ruby の retry はどこからリトライしているのか ?(Ruby 1.8系)

ちなみに

  • 下記は、Ruby 1.9系では構文エラーになる。

知ってちょっとびっくりした

#
# test.rb
#
class RetryTest
  def initialize
    @x = 0
    puts "initialize!!"
  end
  def test x
    @x += 1
    yield @x
    retry if x > @x
  end
end

puts RUBY_VERSION

t = RetryTest.new
t.test(3) { |i| puts "test - #{i}" }
$ ruby test.rb
1.8.7
initialize!!
test - 1
test - 2
test - 3

RetryTest#test は

  • @x をインクリメント
  • ブロックを評価、その際に引数に @x を渡す
  • @x が x を超えない場合は リトライ する

リトライってどこからしているの?

実行結果から、 #test が再実行されている事は明らかだが・・

#
# test.rb
#
class RetryTest
  def initialize
    @x = 0
    puts "initialize!!"
  end
  def test x
    @x += 1
    yield @x
    retry if x > @x
  end
end

def hoge
  puts "#hoge called!"
  3
end

puts RUBY_VERSION

t = RetryTest.new
t.test(hoge) { |i| puts "test - #{i}" }
$ ruby test.rb
1.8.7
initialize!!
#hoge called!
test - 1
#hoge called!
test - 2
#hoge called!
test - 3

どうやら引数も再評価されている。これは予想の上だった。

更にびっくりした

インスタンス生成とメソッド呼び出しを1行で書いてみる

#
# test.rb
#
class RetryTest
  def initialize
    @x = 0
    puts "initialize!!"
  end
  def test x
    @x += 1
    yield @x
    retry if x > @x
  end
end

def hoge
  puts "#hoge called!"
  3
end

puts RUBY_VERSION

RetryTest.new.test(hoge) { |i| puts "test - #{i}" }
$ ruby test.rb
1.8.7
initialize!!
#hoge called!
test - 1
initialize!!
#hoge called!
test - 1
initialize!!
#hoge called!
test - 1
以下延々と続く・・・

リトライのたびにインスタンスが生成されている
引数の評価だけでなく、行全体が再度評価されている模様。
これは初見だとハマると思う。

Ruby 1.9 系ではこんなエラー

$ rvm 1.9.2 test.rb
test.rb:9: Invalid retry
test.rb: compile error (SyntaxError)