Ruby return in yield block called by method, with

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 

I would expect r = 3, but it turns out that it is r = 4. If I delete the security code, r = 3 is expected. Why?

 def foo puts "in foo" s = yield puts "s = #{s}" return 2 end r = bar in foo in bar block => 3 
+5
source share
1 answer

This is the ruby ​​function of "unwinding a stack" of blocks. How your return works step by step:

  • You return 3 from the bar block. return_value = 3 and Ruby notes that this is the return value from the block, so it should unwind the stack and return 3 from the parent function. It would not return to foo at all if there were no ensure section. This is a very important difference between returning from functions and from blocks.
  • But Ruby always does ensure , and there is another return in the ensure foo section.
  • After return 1 in the ensure section of foo , return_value is 1. This is not a value from the block, so Ruby "forgets" the previous return 3 and forgets to return it from bar .
  • It returns 1 from foo and 4 from bar .

In addition, if you write next 3 instead of return 3 in the block, it will return to foo after yield and execute puts "s = #{s}"; return 2 puts "s = #{s}"; return 2 even without a support block. This is Ruby's magic feature for iterators and counters.

+3
source

All Articles