从带有ensure的方法调用的yield块返回Ruby

时间:2015-04-02 11:47:13

标签: ruby

def foo
  puts "in foo"
  s = yield
  puts "s = #{s}"
  return 2
ensure
  puts "in ensure"
  return 1
end

def bar
  foo do
    puts "in bar block"
    return 3
  end
  return 4
end


[36] pry(main)> r = bar
in foo
in bar block
in ensure
=> 4

我希望r = 3但事实证明它是r = 4.如果我删除了确保代码,则预期r = 3。为什么?

def foo
  puts "in foo"
  s = yield
  puts "s = #{s}"
  return 2
end

r = bar
in foo
in bar block
=> 3

1 个答案:

答案 0 :(得分:4)

它是"展开堆栈"的Ruby的特色。从块。您的退货如何逐步运作:

  1. 您从bar区块返回3。 return_value = 3并且Ruby标记它是来自块的返回值,因此它应该展开堆栈并从父函数返回3。如果没有foo部分,则根本不会返回ensure。从函数返回和从块返回是非常重要的区别。
  2. 但Ruby始终执行ensure,而return的{​​{1}}部分中还有一个ensure
  3. foo的{​​{1}}部分return 1之后,ensure为1.它不是来自块的值,因此Ruby"忘记"关于之前的foo并忘记从return_value返回。
  4. 它从return 3返回1,从bar返回foo
  5. 此外,如果你在块中写4而不是bar - 它会在next 3之后返回return 3并执行foo即使没有确保块。这是迭代器和枚举器的神奇Ruby特性。