What an infinite loop is
An infinite loop is a loop that never ends. The program keeps running the body, over and over, and never reaches the code after the loop. Usually this is a bug: the program looks frozen, or fills the terminal with output, until you stop it by hand.
A while loop ends only when its condition becomes false. So an infinite while loop always means one thing: nothing in the loop ever makes the condition false. There are two common ways to get there.
Forgetting the update
The simplest case is a counter that never changes:
let mut i = 1;
while i < 5 {
println!("{}", i);
// i += 1; forgotten
}
The value of i stays at 1, so i < 5 is true on every check, and the program prints 1 forever. Rust helps a little here: because nothing assigns to i, the compiler warns that the variable does not need to be mutable. That warning is a good clue that the update is missing.
A condition the counter jumps over
The second case is subtler, and it is the one in this lesson's exercise. The update is there, but the condition waits for an exact value that the counter never lands on.
The exercise: find the mistake
The program should print the odd numbers from 15 down to 1, and then stop:
let mut counter = 15;
while counter != 0 {
println!("{}", counter);
counter -= 2;
}
It prints 15, 13, 11, 9, 7, 5, 3 and 1 as expected. Then it keeps going: -1, -3, -5, and so on, without end. Follow the counter near the end:
- The counter is 3. The test
3 != 0is true, so 3 is printed and the counter becomes 1. - The counter is 1. The test
1 != 0is true, so 1 is printed and the counter becomes -1. - The counter is -1. Is
-1 != 0? Yes. The loop continues.
The counter started odd and goes down by 2, so it only ever takes odd values. It jumps from 1 to -1 and never equals 0. The condition counter != 0 keeps waiting for a value that is never reached.
The fix: test a range, not one exact value
The loop should stop when the counter is no longer positive. Replace the equality test with a comparison that covers a whole range of values:
let mut counter = 15;
while counter > 0 {
println!("{}", counter);
counter -= 2;
}
Now the loop continues while counter is above 0. After 1 is printed, the counter becomes -1, -1 > 0 is false, and the loop ends. It does not matter that the counter skipped over 0.
The same program with counter >= 1 would also work, because for whole numbers it means the same thing here. What matters is that the comparison is true while you want to continue and becomes false for good once the counter passes the end.
How to check a loop for this bug
- Look at the update. Does it move the variable in the condition at all?
- Look at the direction. A condition like
i < 5needs the counter to increase. A counter that goes down never makes it false. - Look at the step. If the step is bigger than 1, can the counter jump over the value the condition is waiting for? Conditions with
!=or==are the usual suspects. - Trace the last few passes on paper, as done above. The loop's last values show whether the exit is reached.
Unsigned counters
If the counter had an unsigned type such as u32, the problem would be worse, because an unsigned number cannot become -1. In a build with optimizations, as the lessons use, subtracting 2 from 1 wraps around to a very large number, which is still above 0, so even counter > 0 would never be false. In a debug build the subtraction would panic with an overflow error instead. A countdown that steps past the end needs a signed type such as i32, which is what Rust picks for the lesson's counter.
Common mistakes
- Using
!=or==to detect the end of a loop whose step is more than 1. - Updating in the wrong direction, for example
counter += 2in a loop that is meant to count down. - Updating a different variable from the one in the condition.
- Putting the update inside an
ifthat is sometimes not taken.
Key takeaways
- A
whileloop is infinite when its condition can never become false. - Always make sure the loop body moves the condition toward false.
- Prefer range comparisons (
<,<=,>,>=) over exact tests (==,!=) as exit conditions, because a counter with a step greater than 1 may never hit the exact value. - Trace the last few passes by hand to see how the loop ends.
