Loops and other breakable expressions

Syntax
LoopExpression :
   LoopLabel? (
         InfiniteLoopExpression
      | PredicateLoopExpression
      | PredicatePatternLoopExpression
      | IteratorLoopExpression
      | LabelBlockExpression
   )

Rust supports five loop expressions:

All five types of loop support break expressions, and labels. All except labelled block expressions support continue expressions. Only loop and labelled block expressions support evaluation to non-trivial values.

Infinite loops

Syntax
InfiniteLoopExpression :
   loop BlockExpression

A loop expression repeats execution of its body continuously: loop { println!("I live."); }.

A loop expression without an associated break expression is diverging and has type !. A loop expression containing associated break expression(s) may terminate, and must have type compatible with the value of the break expression(s).

Predicate loops

Syntax
PredicateLoopExpression :
   while Expressionexcept struct expression BlockExpression

A while loop begins by evaluating the boolean loop conditional operand. If the loop conditional operand evaluates to true, the loop body block executes, then control returns to the loop conditional operand. If the loop conditional expression evaluates to false, the while expression completes.

An example:

#![allow(unused)]
fn main() {
let mut i = 0;

while i < 10 {
    println!("hello");
    i = i + 1;
}
}

Predicate pattern loops

Syntax
PredicatePatternLoopExpression :
   while let Pattern = Scrutineeexcept lazy boolean operator expression BlockExpression

A while let loop is semantically similar to a while loop but in place of a condition expression it expects the keyword let followed by a pattern, an =, a scrutinee expression and a block expression. If the value of the scrutinee matches the pattern, the loop body block executes then control returns to the pattern matching statement. Otherwise, the while expression completes.

#![allow(unused)]
fn main() {
let mut x = vec![1, 2, 3];

while let Some(y) = x.pop() {
    println!("y = {}", y);
}

while let _ = 5 {
    println!("Irrefutable patterns are always true");
    break;
}
}

A while let loop is equivalent to a loop expression containing a match expression as follows.

'label: while let PATS = EXPR {
    /* loop body */
}

is equivalent to

'label: loop {
    match EXPR {
        PATS => { /* loop body */ },
        _ => break,
    }
}

Multiple patterns may be specified with the | operator. This has the same semantics as with | in match expressions:

#![allow(unused)]
fn main() {
let mut vals = vec![2, 3, 1, 2, 2];
while let Some(v @ 1) | Some(v @ 2) = vals.pop() {
    // Prints 2, 2, then 1
    println!("{}", v);
}
}

As is the case in if let expressions, the scrutinee cannot be a lazy boolean operator expression.

Iterator loops

Syntax
IteratorLoopExpression :
   for Pattern in Expressionexcept struct expression BlockExpression

A for expression is a syntactic construct for looping over elements provided by an implementation of std::iter::IntoIterator. If the iterator yields a value, that value is matched against the irrefutable pattern, the body of the loop is executed, and then control returns to the head of the for loop. If the iterator is empty, the for expression completes.

An example of a for loop over the contents of an array:

#![allow(unused)]
fn main() {
let v = &["apples", "cake", "coffee"];

for text in v {
    println!("I like {}.", text);
}
}

An example of a for loop over a series of integers:

#![allow(unused)]
fn main() {
let mut sum = 0;
for n in 1..11 {
    sum += n;
}
assert_eq!(sum, 55);
}

A for loop is equivalent to a loop expression containing a match expression as follows:

'label: for PATTERN in iter_expr {
    /* loop body */
}

is equivalent to

{
    let result = match IntoIterator::into_iter(iter_expr) {
        mut iter => 'label: loop {
            let mut next;
            match Iterator::next(&mut iter) {
                Option::Some(val) => next = val,
                Option::None => break,
            };
            let PATTERN = next;
            let () = { /* loop body */ };
        },
    };
    result
}

IntoIterator, Iterator, and Option are always the standard library items here, not whatever those names resolve to in the current scope. The variable names next, iter, and val are for exposition only, they do not actually have names the user can type.

Note: that the outer match is used to ensure that any temporary values in iter_expr don't get dropped before the loop is finished. next is declared before being assigned because it results in types being inferred correctly more often.

Loop labels

Syntax
LoopLabel :
   LIFETIME_OR_LABEL :

A loop expression may optionally have a label. The label is written as a lifetime preceding the loop expression, as in 'foo: loop { break 'foo; }, 'bar: while false {}, 'humbug: for _ in 0..0 {}. If a label is present, then labeled break and continue expressions nested within this loop may exit out of this loop or return control to its head. See break expressions and continue expressions.

Labels follow the hygiene and shadowing rules of local variables. For example, this code will print "outer loop":

#![allow(unused)]
fn main() {
'a: loop {
    'a: loop {
        break 'a;
    }
    print!("outer loop");
    break 'a;
}
}

break expressions

Syntax
BreakExpression :
   break LIFETIME_OR_LABEL? Expression?

When break is encountered, execution of the associated loop body is immediately terminated, for example:

#![allow(unused)]
fn main() {
let mut last = 0;
for x in 1..100 {
    if x > 12 {
        break;
    }
    last = x;
}
assert_eq!(last, 12);
}

A break expression is normally associated with the innermost loop, for or while loop enclosing the break expression, but a label can be used to specify which enclosing loop is affected. Example:

#![allow(unused)]
fn main() {
'outer: loop {
    while true {
        break 'outer;
    }
}
}

A break expression is only permitted in the body of a loop, and has one of the forms break, break 'label or (see below) break EXPR or break 'label EXPR.

Labelled block expressions

Syntax
LabelBlockExpression :
   BlockExpression

Labelled block expressions are exactly like block expressions, except that they allow using break expressions within the block. Unlike loops, break expressions within a labelled block expression must have a label (i.e. the label is not optional). Similarly, labelled block expressions must begin with a label.

#![allow(unused)]
fn main() {
fn do_thing() {}
fn condition_not_met() -> bool { true }
fn do_next_thing() {}
fn do_last_thing() {}
let result = 'block: {
    do_thing();
    if condition_not_met() {
        break 'block 1;
    }
    do_next_thing();
    if condition_not_met() {
        break 'block 2;
    }
    do_last_thing();
    3
};
}

continue expressions

Syntax
ContinueExpression :
   continue LIFETIME_OR_LABEL?

When continue is encountered, the current iteration of the associated loop body is immediately terminated, returning control to the loop head. In the case of a while loop, the head is the conditional expression controlling the loop. In the case of a for loop, the head is the call-expression controlling the loop.

Like break, continue is normally associated with the innermost enclosing loop, but continue 'label may be used to specify the loop affected. A continue expression is only permitted in the body of a loop.

break and loop values

When associated with a loop, a break expression may be used to return a value from that loop, via one of the forms break EXPR or break 'label EXPR, where EXPR is an expression whose result is returned from the loop. For example:

#![allow(unused)]
fn main() {
let (mut a, mut b) = (1, 1);
let result = loop {
    if b > 10 {
        break b;
    }
    let c = a + b;
    a = b;
    b = c;
};
// first number in Fibonacci sequence over 10:
assert_eq!(result, 13);
}

In the case a loop has an associated break, it is not considered diverging, and the loop must have a type compatible with each break expression. break without an expression is considered identical to break with expression ().