459

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.

mu is too short
  • 426,620
  • 70
  • 833
  • 800
user169930
  • 4,687
  • 2
  • 18
  • 13

8 Answers8

799

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
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
JRL
  • 76,767
  • 18
  • 98
  • 146
  • 2
    thanks, but next only moves to the next item in the array. is it possible to exit? – user169930 Sep 10 '09 at 00:42
  • You have to call next with the return value. "def f; x = yield; puts x; end" "f do next 3; puts "o"; end" This prints 3 (but no "o") on the console. – Marcel Jackwerth Sep 10 '09 at 01:45
  • 5
    `next`, `break`, `return`, you can't compare – finiteloop Feb 23 '13 at 08:04
  • I added an answer expanding on the comment @MarcelJackwerth added about using `next` or `break` with an argument. – Tyler Holien Jun 10 '13 at 17:54
  • 9
    There's also a keyword called `redo`, which basically just moves execution back to the top of the block within the current iteration. – Ajedi32 Jun 03 '14 at 17:17
  • A little side question: would it still be ok to use break in a File block like this: `File.foreach(filename) { break if condition }` ? Would that leave the file opened? – Redoman Aug 05 '15 at 10:06
  • hi, `returning control to the iterator method`, if the method that execute the block doesn't have iterator outside, how `next, break` behave? Are they the same? – Sam YC Dec 06 '16 at 02:38
70

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.

Don Law
  • 1,329
  • 13
  • 7
42

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
Tyler Holien
  • 4,791
  • 2
  • 19
  • 14
  • 1
    Thanks for the hint with the argument for `break`! – rkallensee Feb 26 '14 at 13:51
  • 2
    could you please update w/ a specific example using `break` (probably just replace one of your `next` with `break` .. – Mike Graf Jul 09 '14 at 16:28
  • One very interesting thing. `break something` works, `break(something)` works but `true && break(somehting)` yields a syntax error. Just FYI. If condition is needed, then `if` or `unless` needs to be used. – akostadinov Oct 27 '16 at 15:53
23

use the keyword break instead of return

AShelly
  • 34,686
  • 15
  • 91
  • 152
8

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
August Lilleaas
  • 54,010
  • 13
  • 102
  • 111
  • 2
    Don't add arbitrary methods like that to the Array class! That's really bad practice. – mrbrdo Oct 25 '12 at 14:36
  • 3
    Rails does it, so why can't he? – wberry Nov 27 '12 at 15:05
  • 2
    @wberry That's not to say that it necessarily _should_. ;) In general, though, it's best to avoid monkey patching core classes unless you have a pretty good reason (i.e. adding some very useful _generalizable_ functionality that lots of other code will find useful). Even then, tread lightly because once a class is heavily monkey-patched, it's easy for libraries to start walking over each other and causing some extremely odd behavior. – blm768 Jun 25 '16 at 06:52
2

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

G. Allen Morris III
  • 1,012
  • 18
  • 30
2

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
smithkm
  • 231
  • 1
  • 9
-4

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

Kiry Meas
  • 1,200
  • 13
  • 26