Return statements inside procs, lambdas, and blocks

voltair picture voltair · Mar 22, 2013 · Viewed 10.3k times · Source

I am having a lot of trouble understanding how return works in blocks, procs, and lambdas.

For instance, in the following case, why does batman_ironman_proc work, while batman_yield throw an error?

def batman_ironman_proc
  victor = Proc.new { return "Batman will win!" }
  victor.call
  "Iron Man will win!"
end

def batman_yield
    yield
    "Iron man will win!"
end

victor = Proc.new { return "Batman will win!" }

puts batman_ironman_proc 
#batman_yield(&victor) === This code throws an error.

Answer

mgibsonbr picture mgibsonbr · Mar 22, 2013

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.