Using 'return' in a Ruby Block

I am trying to use Ruby 1.9.1 for an embedded scripting language, so that the "end user" code is written to the Ruby block. One problem is that I want users to be able to use the return keyword in blocks, so they don’t have to worry about implicit return values. With that in mind, this is what I would like 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 know that this is because this block is a Proc, not a lambda. The code works if I delete 'return', but I really would prefer to use 'return' in this script. Is it possible? I tried to convert the block to lambda, but the result is the same.

+73
ruby lambda return
Feb 24 '10 at 11:15
source share
7 answers

Just 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 a method, but if you check this fragment in irb, you don't have a method, why do you have LocalJumpError
  • break returns the value from the block and ends its call. If your block was called yield or .call , then break also interrupted from this iterator
  • next returns the value from the block and ends its call. If your block was called yield or .call , then next returns the value to the string where yield was called
+149
Feb 24 '10 at 11:55
source share

You cannot do this in Ruby.

The return keyword is always returned from a method or lambda in the current context. In blocks, it will be returned from the method in which the closure was defined. Cannot be forced to return from a call method or lambda.

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

 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 # ... 
+14
Feb 24 '10 at 11:42
source share

You look at it from the wrong point of view. This is a thing , not a lambda.

 def thing(*args, &block) block.call.tap do |value| puts "value=#{value}" end end thing { 6 * 7 } 
+3
Feb 24 '10 at 11:33
source share

Where is the thing called? Are you inside the class?

You can use something like this:

 class MyThing def ret b @retval = b end def thing(*args, &block) implicit = block.call value = @retval || implicit puts "value=#{value}" end def example1 thing do ret 5 * 6 4 end end def example2 thing do 5 * 6 end end end 
+1
Feb 24 '10 at 11:32
source share

I believe this is the correct answer, despite the flaws:

 def return_wrap(&block) Thread.new { return yield }.join rescue LocalJumpError => ex ex.exit_value end def thing(*args, &block) value = return_wrap(&block) puts "value=#{value}" end thing { return 6 * 7 } 

This hack allows users to use the return in their procedures without consequences, the save itself, etc.

The advantage of using Thread here is that in some cases you will not get a LocalJumpError - and the return will occur in the most unexpected place (next to the top-level method, unexpectedly skipping the rest of its body).

The main drawback is the potential invoice (you can replace Thread + join with only yield if that is sufficient in your scenario).

+1
May 27 '15 at 15:16
source share

I had the same problem as the DSL entry for the web framework in ruby ​​... (Anorexic web framework will download!) ...

In any case, I burst into the insides of ruby ​​and found a simple solution using the LocalJumpError returned when the Proc calls were returned ... it has worked well in the tests so far, but I'm not sure if it is fully proven:

 def thing(*args, &block) if block block_response = nil begin block_response = block.call rescue Exception => e if e.message == "unexpected return" block_response = e.exit_value else raise e end end puts "value=#{block_response}" else puts "no block given" end end 

the if statement in the rescue segment might look something like this:

 if e.is_a? LocalJumpError 

but this is uncharted territory for me, so I will stick to what I have tested so far.

0
Oct 11 '14 at 20:55
source share

I found a way, but it involves defining the method as an intermediate step:

 def thing(*args, &block) define_method(:__thing, &block) puts "value=#{__thing}" end thing { return 6 * 7 } 
0
Jul 27 '15 at 14:29
source share



All Articles