Loop Expressions

While Loops

A while loop repeatedly executes its body while a condition is true.

while_expr = "while" expression "{" block "}" ;

The condition expression MUST have type bool.

A while expression has type ().

The condition is evaluated before each iteration. If it is true, the body is executed and the condition is re-evaluated. If it is false, the loop terminates.

fn main() -> i32 {
    let mut sum = 0;
    let mut i = 1;
    while i <= 10 {
        sum = sum + i;
        i = i + 1;
    }
    sum  // 55
}

Infinite Loops

An infinite loop repeatedly executes its body unconditionally.

loop_expr = "loop" "{" block "}" ;

A loop expression has type ! (never), because it never produces a value. Even when break is present, the loop expression itself does not yield a result.

The only way to exit a loop is via break or return.

fn main() -> i32 {
    let mut x = 0;
    loop {
        x = x + 1;
        if x == 5 {
            break;
        }
    }
    x  // 5
}

The loop expression is preferred over while true for infinite loops:

// Preferred
loop {
    // ...
}

// Also valid, but less idiomatic
while true {
    // ...
}

Break and Continue

The break expression exits the innermost enclosing loop.

The continue expression skips to the next iteration of the innermost enclosing loop.

Both break and continue MUST appear within a loop. Using them outside a loop is a compile-time error.

Both break and continue have the never type !.

Currently, break does not carry a value. A loop expression has type ! regardless of whether break is reachable, because the loop itself does not produce a value.

fn main() -> i32 {
    let mut x = 0;
    while true {
        x = x + 1;
        if x == 5 {
            break;
        }
    }
    x  // 5
}
fn main() -> i32 {
    let mut sum = 0;
    let mut i = 0;
    while i < 10 {
        i = i + 1;
        if i % 2 == 0 {
            continue;  // skip even numbers
        }
        sum = sum + i;
    }
    sum  // 25 (1+3+5+7+9)
}

For-In Loops

A for-in loop iterates over a range, binding each element to a loop variable.

for_expr = "for" ["mut"] identifier "in" expression "{" block "}" ;

A for-in expression has type ().

The loop variable is immutable by default. If mut is specified, the loop variable is mutable within each iteration but does not affect the iteration itself.

The iterable expression must be a call to the @range intrinsic. The @range intrinsic accepts 1, 2, or 3 integer arguments:

  • @range(end) — iterates from 0 (inclusive) to end (exclusive) with stride 1
  • @range(start, end) — iterates from start (inclusive) to end (exclusive) with stride 1
  • @range(start, end, stride) — iterates from start (inclusive) to end (exclusive) with the given stride

All arguments to @range MUST have the same integer type.

A for-in loop over @range is equivalent to the following while loop desugaring. The counter is incremented before the body so that continue does not skip the increment:

// for x in @range(start, end, stride) { body }
// is equivalent to:
let mut __counter = start;
while __counter < end {
    let x = __counter;
    __counter = __counter + stride;
    body;
}
fn main() -> i32 {
    let mut sum = 0;
    for i in @range(5) {
        sum = sum + i;
    }
    sum  // 10 (0+1+2+3+4)
}
fn main() -> i32 {
    let mut sum = 0;
    for i in @range(1, 4) {
        sum = sum + i;
    }
    sum  // 6 (1+2+3)
}
fn main() -> i32 {
    let mut sum = 0;
    for i in @range(0, 10, 3) {
        sum = sum + i;
    }
    sum  // 18 (0+3+6+9)
}

break and continue work within for-in loops the same as in while loops: break exits the loop and continue skips to the next iteration.

Array Iteration

A for-in loop can iterate over a fixed-size array. Each element is bound to the loop variable in order.

When the array's element type is a Copy type, each element is copied into the loop variable. The array remains valid after the loop.

When the array's element type is a non-Copy (move) type, each element is moved out of the array. The array is consumed by the loop and is no longer valid after the loop completes.

break inside a for-in loop over an array with non-Copy element type is a compile-time error. Breaking would leave un-iterated elements unconsumed, putting the array in a partially-moved state.

Iterating over an array is equivalent to indexing each element in order:

// for x in arr { body }
// is equivalent to:
let mut __i = 0;
while __i < LENGTH {
    let x = arr[__i];
    __i = __i + 1;
    body;
}
fn main() -> i32 {
    let arr = [10, 20, 30];
    let mut sum = 0;
    for x in arr {
        sum = sum + x;
    }
    sum  // 60
}

Nested Loops

In nested loops, break and continue affect only the innermost enclosing loop.

fn main() -> i32 {
    let mut total = 0;
    let mut outer = 0;
    while outer < 3 {
        let mut inner = 0;
        while true {
            inner = inner + 1;
            total = total + 1;
            if inner == 2 {
                break;  // exits inner loop only
            }
        }
        outer = outer + 1;
    }
    total  // 6
}