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'd expect r = 3 but it turns out it is r = 4. If I remove the ensure code, r = 3 is expected. Why is it?
def foo
puts "in foo"
s = yield
puts "s = #{s}"
return 2
end
r = bar
in foo
in bar block
=> 3
The yield keyword instructs Ruby to execute the code in the block. In this example, the block returns the string "yes!" . An explicit return statement was used in the cool() method, but this could have been implicit as well.
The &block is a way of sending a piece of Ruby code in to a method and then evaluating that code in the scope of that method.
yield is a keyword in Ruby which allow the developer to pass some argument to block from the yield, the number of the argument passed to the block has no limitations, the main advantage of using yield in Ruby, if we face any situation we wanted to our method perform different functions according to calling block, which ...
We can explicitly accept a block in a method by adding it as an argument using an ampersand parameter (usually called &block ). Since the block is now explicit, we can use the #call method directly on the resulting object instead of relying on yield .
It's a Ruby's feature of "unwinding the stack" from blocks. How your return works step by step:
bar
block. return_value
= 3 and Ruby marks that it is a return value from block, so it should unwind the stack and return 3 from parent function. It would not return to foo
at all if there was no ensure
section. It is very important difference between returning from functions and from blocks.ensure
, and there is one more return
in ensure
section of foo
.return 1
in ensure
section of foo
, return_value
is 1. It is not a value from block, so Ruby "forgets" about previous return 3
and forgets to return it from bar
.foo
and 4
from bar
.Moreover, 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
even without the ensure block. This is a magical Ruby feature for iterators and enumerators.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With