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