while / until (Ruby)
Explains while, which repeats while a condition is true, and until, which repeats while a condition is false. Ruby also frequently uses postfix forms and the loop do idiom.
Syntax
# while: repeats while the condition is truthy. while condition # body end # until: repeats while the condition is falsy (inverse of while). until condition # body end # Postfix while: runs the body first, then evaluates the condition (always executes at least once). begin # body end while condition # Postfix until: runs the body first, then repeats while the condition is falsy. begin # body end until condition # loop do: infinite loop. Use break to exit. loop do # body break if exit_condition end
Differences Between while and until
| Syntax | Repeats when | When condition is evaluated | Minimum executions |
|---|---|---|---|
| while condition | Condition is truthy | Before the block (pre-check) | 0 (skipped if condition is false from the start) |
| until condition | Condition is falsy | Before the block (pre-check) | 0 (skipped if condition is true from the start) |
| begin…end while | Condition is truthy | After the block (post-check) | 1 (always executes at least once regardless of condition) |
| begin…end until | Condition is falsy | After the block (post-check) | 1 (always executes at least once regardless of condition) |
| loop do | Always (infinite loop) | — | Repeats until break exits the loop |
Sample Code
while_until_basic.rb
cursed_energy = 100
target = 500
puts "=== Yuji Itadori's training ==="
while cursed_energy < target
cursed_energy += 120
puts "Charging cursed energy... current: #{cursed_energy}"
end
puts "Reached target level #{target}!"
is_sealed = true
wait_count = 0
puts "\n=== Satoru Gojo's seal status ==="
until !is_sealed
wait_count += 1
puts "Turn #{wait_count}: Gojo is sealed..."
is_sealed = false if wait_count >= 3
end
puts "Gojo's seal has been broken!"
puts "\n=== Nobara Kugisaki's hit list ==="
curse_grades = [4, 2, 0, 1, 3, 1, 4]
index = 0
while index < curse_grades.length
grade = curse_grades[index]
index += 1
if grade == 0
puts "Special Grade curse — leaving it to Gojo!"
next
end
puts "Defeated Grade #{grade} curse with Straw Doll Technique."
break if index >= 5
end
puts "Today's mission complete."
ruby while_until_basic.rb === Yuji Itadori's training === Charging cursed energy... current: 220 Charging cursed energy... current: 340 Charging cursed energy... current: 460 Charging cursed energy... current: 580 Reached target level 500! === Satoru Gojo's seal status === Turn 1: Gojo is sealed... Turn 2: Gojo is sealed... Turn 3: Gojo is sealed... Gojo's seal has been broken! === Nobara Kugisaki's hit list === Defeated Grade 4 curse with Straw Doll Technique. Defeated Grade 2 curse with Straw Doll Technique. Special Grade curse — leaving it to Gojo! Defeated Grade 1 curse with Straw Doll Technique. Defeated Grade 3 curse with Straw Doll Technique. Today's mission complete.
post_while_until.rb
shikigami_energy = 0
puts "=== Megumi Fushiguro: shikigami energy refill ==="
begin
shikigami_energy += 150
puts "Injecting cursed energy into Black Dog... current: #{shikigami_energy}"
end while shikigami_energy < 400
puts "Black Dog energy refill complete: #{shikigami_energy}"
mission_accepted = false
attempt = 0
puts "\n=== Kento Nanami: mission acceptance flow ==="
begin
attempt += 1
puts "Reviewing mission (attempt #{attempt}): assessing conditions..."
mission_accepted = true if attempt >= 2
end until mission_accepted
puts "Mission accepted. (accepted after #{attempt} reviews)"
ruby post_while_until.rb === Megumi Fushiguro: shikigami energy refill === Injecting cursed energy into Black Dog... current: 150 Injecting cursed energy into Black Dog... current: 300 Injecting cursed energy into Black Dog... current: 450 Black Dog energy refill complete: 450 === Kento Nanami: mission acceptance flow === Reviewing mission (attempt 1): assessing conditions... Reviewing mission (attempt 2): assessing conditions... Mission accepted. (accepted after 2 reviews)
loop_retry.rb
puts "=== Jujutsu High Surveillance System Starting ==="
tick = 0
threat_detected = false
loop do
tick += 1
puts "[Tick #{tick}] Scanning for cursed spirit activity..."
if tick == 3
threat_detected = true
puts "[Alert] Special Grade cursed spirit detected!"
end
if threat_detected
puts "[Response] Dispatching Grade 1 sorcerer Kento Nanami."
break
end
if tick >= 5
puts "[Normal] No threats. Surveillance complete."
break
end
end
puts "=== Surveillance System Stopped ==="
puts "\n=== Yuji Itadori: cursed energy control training (retry pattern) ==="
attempt = 0
max_attempts = 4
result = loop do
attempt += 1
puts "Training attempt #{attempt}..."
if attempt >= max_attempts
break :failure
end
if attempt == 3
puts "Cursed energy control succeeded!"
break :success
end
puts "Not there yet. Trying again."
end
puts result == :success ? "Training complete!" : "Calling it a day..."
ruby loop_retry.rb === Jujutsu High Surveillance System Starting === [Tick 1] Scanning for cursed spirit activity... [Tick 2] Scanning for cursed spirit activity... [Tick 3] Scanning for cursed spirit activity... [Alert] Special Grade cursed spirit detected! [Response] Dispatching Grade 1 sorcerer Kento Nanami. === Surveillance System Stopped === === Yuji Itadori: cursed energy control training (retry pattern) === Training attempt 1... Not there yet. Trying again. Training attempt 2... Not there yet. Trying again. Training attempt 3... Cursed energy control succeeded! Training complete!
Notes
Ruby's while repeats while the condition is truthy; until repeats while it is falsy. until is completely equivalent to while !condition, but it expresses the intent directly without negation, resulting in more readable code.
The postfix forms begin…end while and begin…end until are post-check loops. The block always executes at least once before the condition is evaluated, guaranteeing a minimum of one execution. They are useful for input validation and cases where an initial operation is mandatory.
loop do is the idiom for an explicit infinite loop. It is suited to service loops with multiple exit conditions and to retry patterns where break's return value is used to receive a result. while true can write the same infinite loop, but loop do is the idiomatic Ruby style. A loop with no break never terminates, so an exit path must always be provided. For a fixed number of repetitions, times / upto / downto is more appropriate.
Common Mistakes
Mistake 1: Forgetting break and creating an infinite loop
When writing a loop with while true or loop do, if the exit condition is not handled correctly the loop runs forever. Typical causes include forgetting to update the counter variable, or placing break in the wrong position.
ng_infinite_loop.rb
cursed_energy = 0
target = 500
while cursed_energy < target
puts "Charging cursed energy... current: #{cursed_energy}"
# cursed_energy is never updated, so the loop never ends.
end
puts "Target reached!"
The loop variable is never updated so the program never terminates. Always update the counter or state variable inside the loop body.
ok_infinite_loop.rb
cursed_energy = 0
target = 500
while cursed_energy < target
cursed_energy += 120
puts "Charging cursed energy... current: #{cursed_energy}"
end
puts "Reached target level #{target}!"
ruby ok_infinite_loop.rb Charging cursed energy... current: 120 Charging cursed energy... current: 240 Charging cursed energy... current: 360 Charging cursed energy... current: 480 Charging cursed energy... current: 600 Reached target level 500!
Mistake 2: Wrong condition direction in begin...end while (using while where until is intended)
When a post-check loop should "repeat until the condition becomes true", begin...end until is the natural expression. Writing it as begin...end while !condition introduces a negation that makes the intent harder to read. Getting the condition direction wrong can also cause the loop to execute zero times or run forever.
ng_begin_end_while.rb
mission_accepted = false
attempt = 0
begin
attempt += 1
puts "Reviewing mission (attempt #{attempt})"
mission_accepted = true if attempt >= 2
end while !mission_accepted
ruby ng_begin_end_while.rb Reviewing mission (attempt 1) Reviewing mission (attempt 2)
The intent "repeat until accepted" can be expressed directly with begin...end until.
ok_begin_end_until.rb
mission_accepted = false
attempt = 0
begin
attempt += 1
puts "Reviewing mission (attempt #{attempt})"
mission_accepted = true if attempt >= 2
end until mission_accepted
puts "Mission accepted. (accepted after #{attempt} reviews)"
ruby ok_begin_end_until.rb Reviewing mission (attempt 1) Reviewing mission (attempt 2) Mission accepted. (accepted after 2 reviews)
Mistake 3: Off-by-one error in while condition
Confusing < with <= in a while condition causes the loop to execute one time too few or one time too many. This often occurs when iterating over an array by index or when a counter crosses a boundary value.
ng_off_by_one.rb
grades = [4, 3, 2, 1, 0]
index = 0
while index < grades.length - 1
puts "Defeated Grade #{grades[index]} curse."
index += 1
end
puts "Total defeated: #{index}"
ruby ng_off_by_one.rb Defeated Grade 4 curse. Defeated Grade 3 curse. Defeated Grade 2 curse. Defeated Grade 1 curse. Total defeated: 4
The last element (index 4) is not processed. Use index < grades.length to iterate over all elements of an array.
ok_off_by_one.rb
grades = [4, 3, 2, 1, 0]
index = 0
while index < grades.length
puts "Defeated Grade #{grades[index]} curse."
index += 1
end
puts "Total defeated: #{index}"
ruby ok_off_by_one.rb Defeated Grade 4 curse. Defeated Grade 3 curse. Defeated Grade 2 curse. Defeated Grade 1 curse. Defeated Grade 0 curse. Total defeated: 5
If you find any errors or copyright issues, please contact us.