An important difference between a proc and a method or lambda is the way in which they handle the return statement. If a method is defined inside another method, the return statement in the inner method exits only from the inner method itself, then the outer method continues executing. The same goes for defining a lambda within a lambda, a lambda inside a method or a method within a lambda. However, when a proc is defined within a method, the return statement will exit from the proc as well as the outer (enclosing) method. Example:
def meditate
puts "Adjusting posture…"
p = Proc.new { puts "Ringing bell…"; return }
p.call
puts "Sitting still…" # This is not executed
end
meditate
Output:
Adjusting posture…
Ringing bell…
Notice how the last line of the method was not executed because the return statement within the proc has exited from both the proc and the enclosing method.
If we define a proc without an enclosing (outer) method and use a return statement, it will throw a LocalJumpError.
p = Proc.new { puts "Ringing bell…"; return }
p.call
Output:
Ringing bell…
LocalJumpError: unexpected return
This happens because when a return statement is reached within a proc, instead of returning from the context where it was called, it returns from the scope on which it (the proc) was defined. In the following example, a LocalJumpError happens because the proc is trying to return from the top-level environment, where it was defined.
def meditate p
puts "Adjusting posture…"
p.call
puts "Sitting still…" # This is not executed
end
p = Proc.new { puts "Ringing bell…"; return }
meditate p
Output:
Adjusting posture…
Ringing bell…
LocalJumpError: unexpected return
Usually, it's not a good idea to use a return statement within a proc. Procs are usually passed around between methods and if the method on which the proc was defined has already returned, it will throw an exception. In the example below we could just remove the return statement. However, there are cases we actually need to return something. In the latter, it's probably best to use a lambda instead of a proc. We will see later that lambdas handle return statements in a different way, more like methods.
Below is another scenario involving return statement:
def zafu_factory
# This method will return the following proc implicitly
Proc.new { puts "Round black zafu"; return }
end
def meditate
puts "Adjusting posture…"
p = zafu_factory
p.call
puts "Sitting still…" # This is not executed
end
meditate
Output:
Adjusting posture…
Round black zafu
LocalJumpError: unexpected return
What just happened? The zafu_factory method created and implicitly returned a proc. Then, the proc was called by the meditate method and when the return statement within the proc was reached, it tried to return from the context on which it was defined (the zafu_factory method). However, zafu_factory already returned the proc and a method can only return once each time it's called. In other words, an exception was thrown because the zafu_factory method had already returned when the proc was called and tried to return a second time.
See more at this blog post about Procs and Lambdas: Closures in Ruby