2.7 Loops

Loops are used to repeat a certain action multiple amounts of times. This could be anything from printing long lists of numbers—as well as it could be infinitely running a program.

There are three different types of loops contained by the Rust language—loop, while and for.

Loop

The Rust loop will loop forever and repeat whatever is inside the code block attached to it—seemingly with no set goal til the end of times.

Here’s an example:

loop
{
    println!("Hello, loop!");
}

The above code will endlessly repeat the same message, over and over again for all of eternity until the world ultimately paintfully ends.

While

The while loop will loop for as long as an expression attached to it evaluates to true:

let mut x = 5;

// Repeat code-block whilst x is less than ten
while x < 10
{
    println!("x is {}.", x);

    // Increase x by one
    x += 1;
}

Running te above code will result in:

x is 5.
x is 6.
x is 7.
x is 8.
x is 9.

The first time we enter the loop, x has the value of five. It loops until x is nine—increases x by one and all of a sudden x < 10 evaluates to false, given that x no longer is less than ten, and so the loop ends.

All the different ways of checking for equality works in the while loop—that is ==, !=, <, >, <= and =>.

For

The for loop is used when a piece of code is supposed to be looped through a certain amount of times—perhaps even based on a couple of conditions.

You may be used to the syntax of a for loop in C-like languages—Rust’s for loop is extremely different compared to it and looks more like higher-level languages such as Python and Lua—yet manages to serve its purpose well.

Rust’s for loop looks this way:

for x in 5..10
{
    println!("x is {}.", x);
}

This will print:

x is 5.
x is 6.
x is 7.
x is 8.
x is 9.

In this example x is set to five. This loop will loopfor as long as x is less than ten—considering the range used in the loop goes from five to ten. More on ranges in a second. It works similarly to how our while loop worked, only the loop does all the dirtywork for us.

5..10 is an expression—or a range—that is converted into an iterator, which holds a sequence of values. In this case, we defined the iterator to hold all values whereas five is the first number, containing every number up until right before ten. The x binding we provided the loop will be set to the current value the iterator gives us over a certain run through the body of the loop—the body being the code block attached to the for loop.

Enumeration

If for some reason we’d like to keep track of how many times the loop has looped, we can make use of a method which is located on the iterator called enumerate.

for (i, x) in (5..10).enumerate()
{
    println!("x is {} on looping {}.", x, i);
}

This will output:

x is 5 on looping 0.
x is 6 on looping 1.
x is 7 on looping 2.
x is 8 on looping 3.
x is 9 on looping 4.

This works in a way similarly to how we used patterns to define multiple variables using one let. The index of the current looping—upon using the enumerate method—gets placed as the first variable we procide the loop with. x works in the exact same way as before.

Break and continue

There also are times when you’ll wish to break out of a loop early—especially when you’re stuck inside a loop loop, which infinitely loops til the end of times unless you break out of it in one way or another.

let mut x = 0;

loop
{
    if x == 4
    {
        break;
    }

    println!("x is {}.", x);

    x += 1;
}

This will print out:

x is 0.
x is 1.
x is 2.
x is 3.

This happens because the loop is broken when the if notices that x is equal to four—the keyword break thus ends the loop early.

Rather than ending the loop early, continue skips everything underneath itself inside the code block attached to a loop, and continues on to the next round of the loop:

for x in 0..10
{
    if x % 2 == 0
    {
        continue;
    }

    println!("x is {}.", x);
}

We use the remaider of x divided by two to find out whether or not x is even or not. If x is an even number, the loop skips the rest of the loop and begins on a new looping.

The above code will print:

x is 1.
x is 3.
x is 5.
x is 7.
x is 9.

Labels

Sometimes we may wish to end a loop that is in one scope—another word for code block—further out than the one we’re currently inside. As you saw from the preavious examples, break and continue normally applies to the loop they’re in when being called. To solve that problem, we give labels—or names—to our loops:

let mut y = 0;

'outer: loop
{
    let mut x = 3;

    loop
    {
        if x == 4
        {
            y += 1;

            if y == 2
            {
                break 'outer;
            }

            break;
        }

        println!("y is {}.", y);

        x += 1;
    }
}

In this example 'outer is the name of our most outer loop. We use that label when ending it from inside another loop that is inside that loop, in order to not end up ending the loop we’re currently in—rather ending the most outer loop that we wish to end. This we achieve using break 'outer.

The above code will output:

y is 0.
y is 1.

See if you can’t understand as to why this is outputted on your own.

Exercises

  • Write a program that prints out a range of ten numbers multiplied by current_number - 1.
  • Make a loop and break out of it once a number reaches one hundred.
  • The coordinates of battleships can be written out using a loop ranging from 10..20. The coordinates for each battleship is always the current number from the range times the current index number. Use enumerate for this.

Moreover

With loops we can do things such as run programs infinitely—giving the user a chance to input something after every looping, perhaps even adding some logic that occurs depending on what the user chooses to do.

Granted you’re one of these people—or androids for that matter—following this as a means of learning Rust, you should probably take yourself one long break—get it?—prior to carrying on unto the next section.