Loops and other breakable expressions
Syntax
LoopExpression :
LoopLabel? (
InfiniteLoopExpression
| PredicateLoopExpression
| PredicatePatternLoopExpression
| IteratorLoopExpression
| LabelBlockExpression
)
Rust supports five loop expressions:
- A
loop
expression denotes an infinite loop. - A
while
expression loops until a predicate is false. - A
while let
expression tests a pattern. - A
for
expression extracts values from an iterator, looping until the iterator is empty. - A labelled block expression runs a loop exactly once, but allows exiting the loop early with
break
.
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
Patternin
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 initer_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 ()
.