Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using 'return' in a Ruby block

I'm trying to use Ruby 1.9.1 for an embedded scripting language, so that "end-user" code gets written in a Ruby block. One issue with this is that I'd like the users to be able to use the 'return' keyword in the blocks, so they don't need to worry about implicit return values. With this in mind, this is the kind of thing I'd like to be able to do:

def thing(*args, &block)   value = block.call   puts "value=#{value}" end  thing {   return 6 * 7 } 

If I use 'return' in the above example, I get a LocalJumpError. I'm aware that this is because the block in question is a Proc and not a lambda. The code works if I remove 'return', but I'd really prefer to be able to use 'return' in this scenario. Is this possible? I've tried converting the block to a lambda, but the result is the same.

like image 839
MetaFu Avatar asked Feb 24 '10 11:02

MetaFu


People also ask

How does return work in Ruby?

Ruby methods ALWAYS return the evaluated result of the last line of the expression unless an explicit return comes before it. If you wanted to explicitly return a value you can use the return keyword.

Is return implicit in Ruby?

Ruby has implicit returns. This means that if a return is the last expression in a path of execution, there's no need for the return keyword. Worth noting is that return 's default argument is nil , which is a falsey value.

What does break return in Ruby?

break is called from inside a loop. It will put you right after the innermost loop you are in. return is called from within methods. It will return the value you tell it to and put you right after where it was called.


2 Answers

Simply use next in this context:

$ irb irb(main):001:0> def thing(*args, &block) irb(main):002:1>   value = block.call irb(main):003:1>   puts "value=#{value}" irb(main):004:1> end => nil irb(main):005:0> irb(main):006:0* thing { irb(main):007:1*   return 6 * 7 irb(main):008:1> } LocalJumpError: unexpected return         from (irb):7:in `block in irb_binding'         from (irb):2:in `call'         from (irb):2:in `thing'         from (irb):6         from /home/mirko/.rvm/rubies/ruby-1.9.1-p378/bin/irb:15:in `<main>' irb(main):009:0> thing { break 6 * 7 } => 42 irb(main):011:0> thing { next 6 * 7 } value=42 => nil 
  • return always returns from method, but if you test this snippet in irb you don't have method, that's why you have LocalJumpError
  • break returns value from block and ends its call. If your block was called by yield or .call, then break breaks from this iterator too
  • next returns value from block and ends its call. If your block was called by yield or .call, then next returns value to line where yield was called
like image 150
MBO Avatar answered Sep 29 '22 10:09

MBO


You cannot do that in Ruby.

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.

The Rubyspec demonstrates that this is indeed the correct behaviour for Ruby (admittedly not a real implementation, but aims full compatibility with C Ruby):

describe "The return keyword" do # ... describe "within a block" do # ... it "causes the method that lexically encloses the block to return" do # ... it "returns from the lexically enclosing method even in case of chained calls" do # ... 
like image 31
molf Avatar answered Sep 29 '22 12:09

molf