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
- while and until loops (and their postfix form)
- loop do ... end with break
- Numeric iterators: times, upto, downto, step
- Skipping with next and exiting with break
- Re-running an iteration with redo
- Why each is preferred over for...in
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 doubledYour 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 hereCommon Errors (and the fix)
- ❌ Infinite loop (condition never changes) → ✅ update the variable inside the body, e.g. count += 1.
- ❌ Off-by-one with times → ✅ remember 3.times yields 0, 1, 2, not 1, 2, 3.
- ❌ Using return to exit a loop in a script → ✅ use break; return exits the whole method.
- ❌ Expecting next to leave the loop → ✅ next only skips one iteration; use break to exit.
- ❌ Relying on a for loop variable leaking out → ✅ it does leak; use each for a clean block scope.
Pro Tips
- 💡 Prefer iterators: n.times and arr.each beat manual counters for clarity.
- 💡 break returns a value: x = loop { break 42 } sets x to 42.
- 💡 step for intervals: 0.step(100, 10) walks in tens without arithmetic in the body.
📋 Quick Reference — Loops
| Concept | Ruby Syntax |
|---|---|
| Condition loop | while x < 10 ... end |
| Inverse loop | until done ... end |
| Forever + break | loop { break if ... } |
| Repeat N times | 3.times { |i| ... } |
| Count up / down | 1.upto(3) , 3.downto(1) |
| Skip / exit | next , break |
🎉 Lesson Complete!
- ✅ while/until loop on a condition; both have a postfix form
- ✅ loop do ... end runs forever until break
- ✅ times, upto, downto, step are the idiomatic counters
- ✅ next skips an iteration; break exits the loop
- ✅ Prefer each over for for clean scoping
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
- Previous: Control Flow
- Next: case/when Conditionals — Branch with === — matching ranges, classes, and regex in when
- Quick reference: Ruby cheat sheet
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.