How to break out from a ruby block?

Viewed 357443

Here is Bar#do_things:

class Bar   
  def do_things
    Foo.some_method(x) do |x|
      y = x.do_something
      return y_is_bad if y.bad? # how do i tell it to stop and return do_things? 
      y.do_something_else
    end
    keep_doing_more_things
  end
end

And here is Foo#some_method:

class Foo
  def self.some_method(targets, &block)
    targets.each do |target|
      begin
        r = yield(target)
      rescue 
        failed << target
      end
    end
  end
end

I thought about using raise, but I am trying to make it generic, so I don't want to put anything any specific in Foo.

8 Answers

I wanted to just be able to break out of a block - sort of like a forward goto, not really related to a loop. In fact, I want to break of of a block that is in a loop without terminating the loop. To do that, I made the block a one-iteration loop:

for b in 1..2 do
    puts b
    begin
        puts 'want this to run'
        break
        puts 'but not this'
    end while false
    puts 'also want this to run'
end

Hope this helps the next googler that lands here based on the subject line.

You have four ways to unwind the stack in 'non-exceptional' ways: next, break, return, and throw.

next will cause the block to return.

break will cause the method that yielded to the block to return.

return will cause the method where the block is defined to return.

throw will traverse up the stack until it finds a catch with a matching symbol, and cause that to return. This is much like a 'lightweight' exception for non-exceptional situations.

All of them can take a return value that will be returned by whatever they caused to return instead of the value their last expression which they would return normally.

Here are some examples:

def doSomething
  puts "> doSomething"
  yield
  puts "< doSomething"
end

def withNext
  puts "> withNext"
  doSomething do
    puts "> block"
    puts "* NEXT! causes the block to return to doSomething"
    next
    puts "< block"
  end
  puts "< withNext"
end

def withBreak
  puts "> withBreak"
  doSomething do
    puts "> block"
    puts "* BREAK! causes doSomething to return to withBreak"
    break
    puts "< block"
  end
  puts "< withBreak"
end

def withReturn
  puts "> withReturn"
  doSomething do
    puts "> block"
    puts "* RETURN! causes withReturn to return"
    return
    puts "< block"
  end
  puts "< withReturn"
end

def withThrow
  puts "> withThrow"
  catch :label do
    puts "> catch :label"
    doSomething do
      puts "> block 1"
      doSomething do
        puts "> block 2"
        puts "* THROW! causes catch :label to return to withThrow"
        throw :label
        puts "< block 2"
      end
      puts "< block 1"
    end
    puts "< catch :label"
  end
  puts "< withThrow"
end

withNext
puts "* Done"
puts
withBreak
puts "* Done"
puts
withReturn
puts "* Done"
puts
withThrow
puts "* Done"

output:

> withNext
> doSomething
> block
* NEXT! causes the block to return to doSomething
< doSomething
< withNext
* Done

> withBreak
> doSomething
> block
* BREAK! causes doSomething to return to withBreak
< withBreak
* Done

> withReturn
> doSomething
> block
* RETURN! causes withReturn to return
* Done

> withThrow
> catch :label
> doSomething
> block 1
> doSomething
> block 2
* THROW! causes catch :label to return to withThrow
< withThrow
* Done
Related