break, next, redo and retry

loop do and break with a value

loop do is an infinite loop that a break ends. The value passed to break becomes the value of the loop, which makes "search until found" one expression:

ruby -e 'i = 0; loop { i += 1; break i if i > 3 }; puts i'

4

ruby -e 'puts [1, 2, 3, 4].each { |n| break n * 10 if n > 2 }'

30

The second line shows that break works in any block, not only in loop: the block stops at 3 and the call returns 30 instead of the array.

next and redo

next skips to the following item, which is what a guard clause at the top of a block does. redo repeats the same item without asking the collection for the next one, which is rare and easy to turn into an endless loop:

ruby -e 'i = 0; while i < 3; i += 1; next if i == 2; print i; end; puts'

13

The two printed digits are 1 and 3: the loop skipped the body for 2 but kept counting. redo would print 2 again without changing i, so use it only when the retry condition changes something inside the body.

retry in a rescue

retry belongs to rescue: it runs the begin block again. It is the shortest way to write "try twice", as long as there is a counter to stop it:

ruby -e 'tries = 0; begin; tries += 1; raise "x" if tries < 2; puts "ok after #{tries}"; rescue; retry; end'

ok after 2

The counter lives outside the begin, so it survives the retry. Without it the program retries forever, which is the usual way a network glitch becomes a hung job.

for is a method in disguise

Ruby still has for, and it is the one loop to avoid: it does not create a new scope, so the loop variable survives after the loop and can overwrite a name the method already uses.

for n in 1..3 puts n end puts n

ruby -e 'for n in 1..3; end; puts n'

3

The each version keeps the variable inside the block, which is why every Ruby guide says to prefer it.

What to watch for

A break in a block passed to a method other than loop returns from that method call with the value, which is fine. A break that the reader does not expect is a hidden early return; keep it close to the condition.

loop catches StopIteration and ends quietly, so an enumerator that runs out does not need a break. Everything else is a while with a clearer condition.

Next: &&, ||, and, or and safe navigation

Article author: Arthur Isaev