开发者

ruby block and returning something from block

I am using ruby 1.8.7.

p = lambda { return 10;}
def lab(block)
  puts 'before'
  puts block.call
  puts 'after'
end
lab p

Above code output is

before
1开发者_JAVA百科0
after

I refactored same code into this

def lab(&block)
  puts 'before'
  puts block.call
  puts 'after'
end
lab { return 10; }

Now I am getting LocalJumpError: unexpected return.

To me both the code are doing same thing. Yes in the first case I am passing a proc and in the second case I am passing a block. But &block converts that block into proc. So proc.call should behave same.

And yes I have seen this post Using 'return' in a Ruby 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.


return inside a block will return from the method the block is in, not from the block. To return from the block use next (it's named that way because with iterator-methods like each and map returning from the block basically means jumping to the next iteration of the loop).

Note that when the return value is the last evaluated expression in the block, you don't need any kind of return statement at all, i.e. lab { 10 } will do the same thing.


The {} block includes the context in which it is given, so the return tries to return from the line lab { return 10; }. You can actually make this work (sometimes even in a useful manner) by placing that line inside a method, which will then return (i.e. "after" is not printed).

To return the 10 to block.call, omit the return (or substitute next).


I think you just need to dereference the block before you pass it:

 foo = lambda { return 10 }

 def trace_block(&fn)
   puts 'before calling fn'
   puts fn.call
   puts 'after caling fn'
 end

 trace_block(&foo)

Output:

  before calling fn
  10
  after caling fn

More info:

  • Understanding Ruby Blocks, Procs and Lambdas
  • Ruby Blocks 101
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜