Start learning free

LESSON 132 OF 414 · WHILE LOOPS & FOR LOOPS

Infinite For Loops in Rust

Lesson Read the full explanation

Remember the infinite while loop? Well, there is also such a thing called the infinite for loop. In Rust, a for loop over a range always ends by itself, but a loop built like a classic for loop (set up a variable, check a condition, update the variable) can still run forever.

Here is an example:

let mut i = 0;
while i < 10 {
    println!("{}", i);
    i -= 1;
    i += 1; // the "update" step of a classic for loop
}

This is what happens during this loop:

0. Creates and initializes a variable i = 0

1. First iteration:
	a. Is i < 10 true? <=> Is 0 < 10 true? Yes.
	b. Run the code inside {}:
        1) println!("{}", i); => Output: 0
	      2) i -= 1; => i becomes -1
  c. i += 1; => i becomes 0 again

2. Second iteration:
	a. Is i < 10 true? <=> Is 0 < 10 true? Yes.
	b. Run the code inside {}:
        1) println!("{}", i); => Output: 0
	      2) i -= 1; => i becomes -1
  c. i += 1; => i becomes 0 again

3. Third iteration:
	a. Is i < 10 true? <=> Is 0 < 10 true? Yes.
	b. Run the code inside {}:
        1) println!("{}", i); => Output: 0
	      2) i -= 1; => i becomes -1
  c. i += 1; => i becomes 0 again

...

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

The program would basically print 0 forever never exiting the loop.


Assignment
Follow the Coding Tutorial and let's practice with infinite for 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 For Loops in Rust, explained

    What an infinite loop is

    A loop repeats its body as long as its condition holds. If nothing inside the loop 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. It is nearly always a bug, with one important exception: a loop that is meant to run until something inside it decides to leave.

    Rust has no C-style for (init; condition; update) loop. Its for loop walks over an iterator such as a range, and it ends by itself when the iterator runs out of values. The infinite loops you meet in Rust therefore come from while loops, whose condition and update you write yourself, or from loop, which has no condition at all.

    How a while loop gets stuck

    The lesson's exercise starts with this code, which should print the odd numbers from 15 down to 1:

    let mut number = 15;
    while number != 0 {
        println!("{}", number);
        number += 1;
    }
    

    Run it and it prints 15, 16, 17, 18 and keeps going. Two things are wrong, and each one alone would be enough to cause trouble.

    1. The variable moves the wrong way. The goal is to count down, but number += 1 makes it grow. Starting at 15 and growing, number moves away from 0 and can never equal it, so number != 0 stays true.
    2. The condition can never be reached. Even with number -= 2, the values are 15, 13, 11, ..., 3, 1, -1. Subtracting 2 from an odd number never lands on 0, so the loop would step over the stopping value and carry on into negative numbers.

    The second point is a common trap. A condition written with != only stops the loop if the variable hits that exact value. A condition such as number > 0 stops once the variable has passed the limit, so it is safer when the step is not 1.

    Fixing it with a for loop over a range

    A range with adapters describes the whole sequence in one place, so there is no variable to forget to update:

    for number in (1..=15).rev().step_by(2) {
        println!("{}", number);
    }
    

    This prints 15, 13, 11, 9, 7, 5, 3 and 1, each on its own line, and then stops. Read the pieces from left to right:

    • 1..=15 is an inclusive range: it contains 1, 2, ..., 15. The ordinary 1..15 would leave out 15.
    • .rev() reverses it, so the values come out as 15, 14, ..., 1.
    • .step_by(2) keeps the first value and then every second one after it, so 15, 13, 11, and so on.
    • number is created by the for itself. It needs no let mut before the loop and no update inside it, and the body cannot change where the loop goes next.

    The order of the adapters matters

    step_by counts from the start of whatever it is given. After .rev() the sequence starts at 15, so every second value is odd. With a different top value, (1..=14).rev().step_by(2) prints 14, 12, 10, ..., 2: the same code produces even numbers, because the first value is now 14.

    Writing (1..=15).step_by(2).rev() does not compile for an inclusive range of i32: the compiler reports that RangeInclusive<i32> does not implement ExactSizeIterator, which rev() needs after step_by here. Reverse first, then step.

    Infinite loops on purpose

    Sometimes you want a loop with no natural end, such as one that keeps reading input until it sees a stop signal. Rust has a dedicated keyword for it:

    let mut k = 0;
    loop {
        k += 1;
        if k * k > 50 {
            break;
        }
    }
    println!("{}", k);
    

    This prints 8. loop repeats forever, and break is the only way out. Writing loop instead of while true tells both the reader and the compiler that the loop is meant to be endless, and the compiler knows that code after a loop with no break can never run.

    Common mistakes

    • Forgetting the update. A while loop that never changes the variable in its condition repeats forever.
    • Updating in the wrong direction. Counting down needs -=, counting up needs +=. Check that every pass brings the variable closer to the stopping condition.
    • Using != with a step bigger than 1. The variable can jump over the target value and never match it.
    • Assuming a range is inclusive. a..b stops before b; a..=b includes it.
    • Looping over a vector by value. for n in nums moves the vector into the loop, so nums can't be used afterwards. Use for n in &nums to borrow it.

    Key takeaways

    • A loop is infinite when nothing inside it can make its condition false.
    • Rust's for loop iterates over an iterator, so it ends when the iterator is exhausted; the infinite loops are while loops with a bad update and deliberate loop blocks.
    • Prefer <, > and their variants to != as a stopping condition when the step isn't 1.
    • (1..=15).rev().step_by(2) counts down from 15 to 1 by twos, with no variable to maintain.
    • Use loop with break when you want a loop that ends from inside.
    Keep going with RustAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free