How to break out from a ruby block?

Ruby

Ruby Problem Overview


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.

Ruby Solutions


Solution 1 - Ruby

Use the keyword next. If you do not want to continue to the next item, use break.

When next is used within a block, it causes the block to exit immediately, returning control to the iterator method, which may then begin a new iteration by invoking the block again:

f.each do |line|              # Iterate over the lines in file f
  next if line[0,1] == "#"    # If this line is a comment, go to the next
  puts eval(line)
end

When used in a block, break transfers control out of the block, out of the iterator that invoked the block, and to the first expression following the invocation of the iterator:

f.each do |line|             # Iterate over the lines in file f
  break if line == "quit\n"  # If this break statement is executed...
  puts eval(line)
end
puts "Good bye"              # ...then control is transferred here

And finally, the usage of return in a block:

return always causes the enclosing method to return, regardless of how deeply nested within blocks it is (except in the case of lambdas):

def find(array, target)
  array.each_with_index do |element,index|
    return index if (element == target)  # return from find
  end
  nil  # If we didn't find the element, return nil
end

Solution 2 - Ruby

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.

Solution 3 - Ruby

If you want your block to return a useful value (e.g. when using #map, #inject, etc.), next and break also accept an argument.

Consider the following:

def contrived_example(numbers)
  numbers.inject(0) do |count, x|
    if x % 3 == 0
      count + 2
    elsif x.odd?
      count + 1
    else 
      count
    end
  end
end

The equivalent using next:

def contrived_example(numbers)
  numbers.inject(0) do |count, x|
    next count if x.even?
    next (count + 2) if x % 3 == 0
    count + 1
  end
end

Of course, you could always extract the logic needed into a method and call that from inside your block:

def contrived_example(numbers)
  numbers.inject(0) { |count, x| count + extracted_logic(x) }
end

def extracted_logic(x)
  return 0 if x.even?
  return 2 if x % 3 == 0
  1
end

Solution 4 - Ruby

use the keyword break instead of return

Solution 5 - Ruby

Perhaps you can use the built-in methods for finding particular items in an Array, instead of each-ing targets and doing everything by hand. A few examples:

class Array
  def first_frog
    detect {|i| i =~ /frog/ }
  end
  
  def last_frog
    select {|i| i =~ /frog/ }.last
  end
end

p ["dog", "cat", "godzilla", "dogfrog", "woot", "catfrog"].first_frog
# => "dogfrog"
p ["hats", "coats"].first_frog
# => nil
p ["houses", "frogcars", "bottles", "superfrogs"].last_frog
# => "superfrogs"

One example would be doing something like this:

class Bar
  def do_things
    Foo.some_method(x) do |i|
      # only valid `targets` here, yay.
    end
  end
end

class Foo
  def self.failed
    @failed ||= []
  end

  def self.some_method(targets, &block)
    targets.reject {|t| t.do_something.bad? }.each(&block)
  end
end

Solution 6 - Ruby

next and break seem to do the correct thing in this simplified example!

class Bar
  def self.do_things
      Foo.some_method(1..10) do |x|
            next if x == 2
            break if x == 9
            print "#{x} "
      end
  end
end

class Foo
    def self.some_method(targets, &block)
      targets.each do |target|
        begin
          r = yield(target)
        rescue  => x
          puts "rescue #{x}"
        end
     end
   end
end

Bar.do_things

output: 1 3 4 5 6 7 8

Solution 7 - Ruby

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

Solution 8 - Ruby

To break out from a ruby block simply use return keyword return if value.nil? next.

  • next terminates lambda, block, or proc it is in.
  • break terminates the method that yielded to the block or invoked the proc or lambda it is in.

Credit to: Ruby block return, break, next

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser169930View Question on Stackoverflow
Solution 1 - RubyJRLView Answer on Stackoverflow
Solution 2 - RubyDon LawView Answer on Stackoverflow
Solution 3 - RubyTyler HolienView Answer on Stackoverflow
Solution 4 - RubyAShellyView Answer on Stackoverflow
Solution 5 - RubyAugust LilleaasView Answer on Stackoverflow
Solution 6 - RubyG. Allen Morris IIIView Answer on Stackoverflow
Solution 7 - RubysmithkmView Answer on Stackoverflow
Solution 8 - RubyKiry MeasView Answer on Stackoverflow