What an infinite loop is
A while loop repeats its body as long as its condition is true. If nothing inside the body ever makes the condition false, the loop never ends and the program keeps running until it is stopped from outside. That is an infinite loop.
The classic cause is forgetting to update the variable the condition depends on:
var i = 1
while i < 5 {
print(i)
// i += 1 // forgot this
}
Here i stays 1, so i < 5 is true on every pass and the program prints 1 forever. Adding i += 1 inside the body fixes it: the loop prints 1, 2, 3, 4 and stops when i reaches 5.
A subtler cause: waiting for a value that is skipped
The lesson's exercise shows a second way to get stuck. The loop updates its counter correctly, but the condition waits for one exact value that the counter jumps over:
var counter = 15
while counter != 0 {
print(counter)
counter -= 2
}
The goal is to print the odd numbers from 15 down to 1. Follow the counter through the last passes: 3, then 1, then -1. Subtracting 2 from an odd number always gives an odd number, so counter is never 0. The condition counter != 0 is true on every pass, and the loop goes on with -1, -3, -5 and so on.
Swift's Int is 64 bits and arithmetic that overflows crashes the program, so this loop would not run forever in the strict sense. It would count down for an astronomically long time and then trap, which is no better in practice.
Fixing the exercise
- Find what the loop is waiting for. The condition waits for
counterto equal 0, and that never happens. - Ask what you really mean. You want to keep going while there are positive numbers left, not until you hit one particular number.
- Use a range check. Replace
!=with>. Once the counter passes below 1, the condition is false and the loop ends, whether or not the counter lands on a specific value.
var counter = 15
while counter > 0 {
print(counter)
counter -= 2
}
This prints 15, 13, 11, 9, 7, 5, 3 and 1. After printing 1 the counter becomes -1, the condition -1 > 0 is false, and the loop stops.
Why != is risky and > is safer
A condition with != only stops the loop if the counter lands exactly on the target. That works when the step is 1, but it breaks as soon as the step is larger or the start value changes. The same loop started at 14 reaches 0 and stops:
var d = 14
while d != 0 {
print(d, terminator: " ")
d -= 2
}
14 12 10 8 6 4 2
So the code looks right for one starting value and hangs for another. A comparison such as >, <, >= or <= describes a whole range of values, and the loop ends once the counter leaves that range, however it got there.
How to avoid infinite loops
- Check the update. Every variable in the condition should change inside the body, and it should change in the direction that makes the condition false.
- Prefer range comparisons. Use
<,>,<=or>=when a counter moves toward a limit. - Trace the last passes by hand. Write down the counter for the final few iterations and check that the condition really turns false.
- Use a
forloop when you know the range. Aforover a range or astrideends by itself and has no counter for you to forget.
Key takeaways
- A
whileloop ends only when its condition becomesfalse. - Forgetting to update the counter, or updating it in the wrong direction, makes the loop infinite.
- A counter that skips over the exact value in an
!=condition never stops the loop. - A range check such as
counter > 0stops the loop even when the counter jumps past the boundary.
