Start learning free

LESSON 127 OF 414 · WHILE LOOPS & FOR LOOPS

Infinite While Loops in Rust: Why Conditions Must Be Reachable

Lesson Read the full explanation

Loops bring great power but also great responsibility. The biggest danger when using loops is writing an infinite loop.

An infinite loop is a loop that never terminates. For example, if we forget to increment i in our previous example:

let mut i = 1;
while i < 5 {
    println!("{}", i);
    // i += 1; => forgot this
}

This program will never terminate as i will always have value 1 and thus the condition i < 5 will always be satisfied.

The program would basically print 1 forever never exiting the while loop.


Pro Tip:

As programmers, we want to make sure we never write infinite loops as they make our program run forever and completely unusable.


Assignment
Follow the Coding Tutorial and practice with infinite while loops!


Hint
Look at the examples above if you get stuck.


STEP 1 OF 1
    Ctrl + Enter to check

    RUST · WHILE LOOPS & FOR LOOPS

    Infinite While Loops in Rust, explained

    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:

    1. The counter is 3. The test 3 != 0 is true, so 3 is printed and the counter becomes 1.
    2. The counter is 1. The test 1 != 0 is true, so 1 is printed and the counter becomes -1.
    3. 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 < 5 needs 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 += 2 in a loop that is meant to count down.
    • Updating a different variable from the one in the condition.
    • Putting the update inside an if that is sometimes not taken.

    Key takeaways

    • A while loop 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.
    Keep going with RustAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free