Using return keyword in Ruby block
Herein lies one of the primary differences between a block/Proc and a lambda/Method (the other primary difference would be arity). If you don't want a call to return
to exit the method, that would infer that you expect that block should be self-contained in its flow control, and be treated as an encapsulated method.
This description is essentially what a lambda is - an anonymous method. However, a standard ruby block is essentially an anonymous Proc, and takes nothing away from the flow control of the method.
As mentioned in the comments, you may be able to use next
to escape the block without returning control away from the method. 'May' because next
may just continue to the next item that the method iterator is passing to the block.
Related to this, see http://yehudakatz.com/2012/01/10/javascript-needs-blocks/ and Differences between Proc and Lambda
Return statements inside procs, lambdas, and blocks
As one answer in the linked question shows:
The
return
keyword always returns from the method or lambda in the current context. In blocks, it will return from the method in which the closure was defined. It cannot be made to return from the calling method or lambda.
Your first example was successful because you defined victor
in the same function you wanted to return from, so a return
was legal in that context. In your second example, victor
was defined in the top-level. The effect of that return
, then, would not be to return from batman_yield
(the calling method), but [if it were valid] to return from the top-level itself (where the Proc
was defined).
Clarification: while you can access the return value of a block (i.e. "The value of the last expression evaluated in the block is passed back to the method as the value of the yield" - as per your comment), you can't use the return
keyword, for the reason stated above. Example:
def batman_yield
value = yield
return value
"Iron man will win!"
end
victor = Proc.new { return "Batman will win!" }
victor2 = Proc.new { "Batman will win!" }
#batman_yield(&victor) === This code throws an error.
puts batman_yield(&victor2) # This code works fine.
Return the value from Ruby block
Use call
.
block.call
if block
takes arguments, then give arguments:
block.call(whatever_arguments)
ruby block and returning something from block
When you pass in the block with &, you're converting it to a proc. The important point is that a proc and a lambda are different (lambda is actually a subclass of proc), specifically in how they deal with return.
So your refactored code is actually the equivalent of:
p = Proc.new { return 10;}
def lab(block)
puts 'before'
puts block.call
puts 'after'
end
lab p
which also generates a LocalJumpError.
Here's why: A proc's return returns from its lexical scope, but a lambda returns to its execution scope. So whereas the lambda returns to lab
, the proc passed into it returns to the outer scope in which it was declared. The local jump error means it has nowhere to go, because there's no enclosing function.
The Ruby Programming Language says it best:
Procs have block-like behavior and lambdas have method-like behavior
You just have to keep track of what you're using where. As others have suggested, all you need to do is drop the return
from your block, and things will work as intended.
Ruby return in yield block called from a method with ensure
It's a Ruby's feature of "unwinding the stack" from blocks. How your return works step by step:
- You return 3 from
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 tofoo
at all if there was noensure
section. It is very important difference between returning from functions and from blocks. - But Ruby always executes
ensure
, and there is one morereturn
inensure
section offoo
. - After
return 1
inensure
section offoo
,return_value
is 1. It is not a value from block, so Ruby "forgets" about previousreturn 3
and forgets to return it frombar
. - It returns 1 from
foo
and4
frombar
.
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.
How can I return something early from a block?
next
inside a block returns from the block. break
inside a block returns from the function that yielded to the block. For each
this means that break
exits the loop and next
jumps to the next iteration of the loop (thus the names). You can return values with next value
and break value
.
Related Topics
Ruby Operator Precedence Table
Confusion With the Assignment Operation Inside a Falsy 'If' Block
Rails :Dependent =≫ :Destroy VS :Dependent =≫ :Delete_All
No Such File to Load - Rubygems (Loaderror)
Your Ruby Version Is 2.0.0, But Your Gemfile Specified 2.1.0
Best Way to Create Custom Config Options For My Rails App
What Exactly Is the Singleton Class in Ruby
In Ruby on Rails, How to Format a Date With the "Th" Suffix, as In, "Sun Oct 5Th"
How to Create a Deep Copy of an Object in Ruby
Is There a "Do ... While" Loop in Ruby
How to Install Postgresql'S Pg Gem on Ubuntu
How to Find Which Operating System My Ruby Program Is Running On