Loops (while, until, loop)

Reviewed & published by Brayan K

A loop is a control structure that repeats a block of code, and Ruby gives you several flavours — while and until repeat based on a condition, while loop repeats forever until you break out.

Part of the free Ruby course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

You'll also meet the idiomatic numeric iterators times, upto, downto, and step, plus the flow keywords next, break, and redo.

What You'll Learn in This Lesson

1️⃣ while & until

A while loop repeats as long as its condition stays true; until is the inverse, repeating until the condition becomes true. Both can be written after a single statement as a compact postfix loop. Just remember to change the condition's variable inside the body, or you'll loop forever.

# while runs WHILE a condition is true; until runs UNTIL it becomes true
count = 1
while count <= 3
  puts "while: #{count}"
  count += 1
end

n = 3
until n == 0          # same as: while n != 0
  puts "until: #{n}"
  n -= 1
end

# Postfix form: put the loop after a single statement
i = 0
i += 1 while i < 3
puts "i ended at #{i}"

2️⃣ loop do ... end

loop runs its block endlessly, so you control the exit with break. It shines when the stop condition is decided in the middle of the body — like retry-until-success. It also auto-handles StopIteration, which makes it pair nicely with an enumerator's .next.

# loop do ... end runs forever — you exit with break
total = 0
loop do
  total += 10
  break if total >= 30     # leave the loop
end
puts "total = #{total}"

# loop automatically stops on StopIteration from an enumerator
nums = [1, 2, 3].each
loop do
  puts nums.next            # 1, 2, 3 then StopIteration ends the loop
end
puts "done"

3️⃣ Numeric Iterators & Flow Control

When you want to repeat a fixed number of times, reach for times, upto, downto, or step rather than a manual counter — they're clearer and harder to get wrong. Inside any loop, next skips to the next iteration and break leaves entirely.

# Numeric iterators are the idiomatic way to "loop N times"
3.times { |i| puts "times #{i}" }     # 0, 1, 2

1.upto(3) { |i| puts "up #{i}" }      # 1, 2, 3
3.downto(1) { |i| puts "down #{i}" }  # 3, 2, 1

0.step(10, 5) { |i| puts "step #{i}" } # 0, 5, 10
# next, break, and redo control the flow inside a loop
(1..6).each do |n|
  next if n.even?          # skip the rest of THIS iteration
  break if n > 4           # leave the loop entirely
  puts n                   # prints 1, 3 (5 would break first via n>4)
end

# next can also supply a value back to map
doubled = [1, 2, 3, 4].map do |n|
  next 0 if n.odd?         # odd numbers map to 0
  n * 2
end
p doubled

Your turn. Fill in each ___ blank with the right keyword or method, then run it.

# 🎯 YOUR TURN — fill in the blanks, then run with: ruby drain.rb

balance = 100
months  = 0
___ balance <= 0          # 👉 keyword that loops UNTIL the condition is true
  balance -= 30
  months  += 1
end
puts "Empty after #{months} months."

5.___(1) { |n| puts "T-minus #{n}" }   # 👉 count DOWN from 5 to 1
puts "Liftoff!"

# ✅ Expected output:
#    Empty after 4 months.
#    T-minus 5
#    T-minus 4
#    T-minus 3
#    T-minus 2
#    T-minus 1
#    Liftoff!

Mini-Challenge: A Guess Checker

Walk a list of guesses with loop, using next to print a hint and continue, and break to stop on the correct answer. Run with ruby guess.rb.

# 🎯 MINI-CHALLENGE: a number-guessing checker.
# Save as guess.rb and run with: ruby guess.rb
#
# secret = 7 and guesses = [3, 9, 7, 1].
# Use loop do ... end and walk the guesses:
#   - print "<g> is too low"  and next, if the guess is below the secret
#   - print "<g> is too high" and next, if it is above
#   - otherwise print "<g> is correct!" and break.
#
# ✅ Example output:
#    3 is too low
#    9 is too high
#    7 is correct!

# your code here

Common Errors (and the fix)

Pro Tips

📋 Quick Reference — Loops

ConceptRuby Syntax
Condition loopwhile x < 10 ... end
Inverse loopuntil done ... end
Forever + breakloop { break if ... }
Repeat N times3.times { |i| ... }
Count up / down1.upto(3) , 3.downto(1)
Skip / exitnext , break

🎉 Lesson Complete!

Practice quiz

A while loop keeps running as long as its condition is:

  • false
  • nil
  • true
  • zero

Answer: true. while repeats while the condition stays true.

until x == 0 is equivalent to which while loop?

  • while x != 0
  • while x == 0
  • while x > 0
  • while x

Answer: while x != 0. until runs while the condition is false, so until x == 0 is while x != 0.

How do you exit a loop do ... end block?

  • return
  • stop
  • exit
  • break

Answer: break. loop runs forever until you break out of it.

What numbers does 3.times yield to its block?

  • 1, 2, 3
  • 0, 1, 2
  • 0, 1, 2, 3
  • 3, 2, 1

Answer: 0, 1, 2. times is zero-based: 3.times yields 0, 1, 2.

What does 1.upto(3) iterate over?

  • 1, 2, 3
  • 0, 1, 2
  • 1, 2
  • 3, 2, 1

Answer: 1, 2, 3. upto counts inclusively upward: 1, 2, 3.

Which counts downward from 3 to 1?

  • 3.times
  • 1.upto(3)
  • 3.downto(1)
  • 3.step(1)

Answer: 3.downto(1). downto counts down inclusively: 3, 2, 1.

What does next do inside a loop?

  • exits the loop
  • skips the rest of the current iteration
  • restarts the program
  • returns from the method

Answer: skips the rest of the current iteration. next jumps to the next iteration, like continue.

Inside loop, why does pairing it with an enumerator's .next stop cleanly?

  • loop ignores errors
  • enumerators never end
  • next returns false at the end
  • loop quietly rescues StopIteration

Answer: loop quietly rescues StopIteration. loop rescues StopIteration, so it ends gracefully at the last element.

What is the value of x = loop { break 42 }?

  • nil
  • 42
  • true
  • 0

Answer: 42. break can return a value, which becomes the loop's result.

Why is for...in discouraged compared with each?

  • it is slower
  • it cannot iterate arrays
  • its loop variable leaks out because for has no new scope
  • it is deprecated syntax

Answer: its loop variable leaks out because for has no new scope. for does not create a new scope, so its variable leaks; each keeps it local.

Continue this course

Frequently asked questions

What's the difference between while and until?

They're mirror images. while keeps looping as long as its condition is true and stops when it becomes false. until keeps looping as long as its condition is false and stops when it becomes true — so until x == 0 is the same as while x != 0. Use whichever reads more naturally: until the queue is empty often reads better than while the queue is not empty. Both also have a postfix form, like print(x) while x < 10, for one-liners.

When should I use loop do ... end?

Use loop when you don't know in advance how many iterations you need and the exit condition lives in the middle or end of the body — for example, retrying until something succeeds, or reading input until a sentinel value. You break out of it explicitly. A bonus: loop quietly rescues StopIteration, so pairing it with an enumerator's .next stops cleanly at the end of a collection instead of raising.

What do next, break, and redo do?

next skips the rest of the current iteration and moves to the next one — like continue in other languages; inside map you can even write next value to control what that element maps to. break exits the loop immediately and can return a value (the result of break x becomes the loop's value). redo re-runs the current iteration from the top without re-evaluating the loop condition or advancing — handy for retrying a single step, though it's used far less often than next and break.

Why is for...in discouraged in Ruby?

Two reasons. First, idiom: Rubyists overwhelmingly prefer iterator methods like each, times, upto, and map because they read clearly and compose with blocks. Second, scope: a for loop does NOT create a new variable scope, so its loop variable leaks out and is still visible after the loop ends, whereas a block-based each keeps its parameter local. Reaching for .each (and the numeric iterators in this lesson) gives you cleaner, more predictable code.