理解Proc中的return引起的LocalJumpError

def procBuilder(message)
Proc.new{ puts message;return}
end
def test puts "entering method" p = procBuilder("entering proc") p.call puts "exit method"
end test

By design, this is to throw a LocalJumpError, but I don't rightly understand why. If I had to guess what this did, I would guess it would initially print "entering proc" upon p = procBuilder("entering proc") running then throw an error on p.call as there is no string being passed by p.call, but clearly I'm missing something critical that is occurring between those 2 lines. I also don't completely understand why this works with a lambda rather than a proc but I imagine understanding the error will resolve that issue as well.

 

Correct me if I am wrong, but I think it has something to do with the return statement. return in a proc will try to return from the calling method as well. So p.call tries to return from test as well as procbuilder.

 

Here's an answer I gave to a related question. It talks a bit about lambda vs proc and LocalJumpErrors.

In a proc, return is a special piece of syntax that returns from the lexical scope of the proc, not the proc itself. So it's trying to return out of procBuilder, which has already exited.

There are a couple ways to fix this:

  1. Don't use return at all. Ruby will return control to proc's caller all on its own.
  2. Change proc to lambda, which behaves the way you expect. Lambdas act like methods; procs act like blocks.

As for the error you're expecting, you shouldn't get that. procBuilder returns a proc that encloses the message variable. You don't need any arguments to the proc itself.

Edit: answering your additional question. The proc is a closure. It has "captured" the message variable (a local variable in procBuilder), which was in scope when the proc was created. The proc now can wander through your program with the message variable hidden inside of it, ready to be printed when you call it. The only trouble is the return statement, which has the additional requirement that it the lexical scope still be "live".

The reason for all this is that this behavior is really helpful in blocks. In this case, it's not helpful at all, so you should just use a lambda, where return means something less insane.

 

a varing of the above code sample:

def test
puts "entering method"
p = Proc.new{ puts "entering proc"; return}
p.call
puts "exit method"
end

test

 

 

 

 

 

 

posted on 2013-11-23 15:21  优雅的码农  阅读(110)  评论(0)    收藏  举报

导航