LaunchKit
All quizzes
Ruby 0 views

return inside a lambda vs inside a proc

Question

What's the return value of the following Ruby code?

def procs_return
  -> { return :lambda }.call

  proc { return :proc }.call

  return :return
end

procs_return # => ???

The correct answer is

Explanation

TL;DR

return inside a lambda returns from the lambda only. return inside a proc returns from the method where the proc was defined. So the lambda call completes and the method keeps going, then the proc's return :proc exits procs_return itself. The final return :return never runs and the method returns :proc.

Step by step

def procs_return
  -> { return :lambda }.call

  proc { return :proc }.call

  return :return
end

procs_return # => :proc

The lambda behaves like a miniature method: its return just produces the lambda's own return value, and execution resumes on the next line of procs_return:

def lambda_only
  result = -> { return :lambda }.call
  [result, :method_continues]
end

lambda_only # => [:lambda, :method_continues]

The proc has no such boundary. Its return belongs to the enclosing method, exactly as if it had been written inline in the method body. When proc { return :proc }.call runs, procs_return returns :proc on the spot.

Both objects are instances of Proc; the flag that changes the semantics is visible through Proc#lambda?:

-> {}.lambda?    # => true
proc {}.lambda?  # => false

Edge cases

A proc's return targets the method frame that created the proc. If that frame is already gone when the proc is called, there is nothing to return from and Ruby raises LocalJumpError:

def make_proc
  proc { return :oops }
end

orphan = make_proc

begin
  orphan.call
rescue LocalJumpError => e
  e.message # => "unexpected return"
end

This is why :proc beats the LocalJumpError choice here: the proc in the snippet is called while procs_return is still on the stack, so its return has a live method to unwind.

Share this quiz

Comments

No comments yet. Be the first.

Only used to confirm and publish your comment. Never shown publicly, never shared.

Markdown: **bold**, `code`, ```fenced blocks```, > quotes, [links](url). HTML and images are not rendered.