Loops
Syntax
LoopExpression :
LoopLabel? (
InfiniteLoopExpression
| PredicateLoopExpression
| PredicatePatternLoopExpression
| IteratorLoopExpression
)
Rust supports four loop expressions:
- A
loopexpression denotes an infinite loop. - A
whileexpression loops until a predicate is false. - A
while letexpression tests a pattern. - A
forexpression extracts values from an iterator, looping until the iterator is empty.
All four types of loop support break expressions,
continue expressions, and labels.
Only loop supports evaluation to non-trivial values.
Infinite loops
Syntax
InfiniteLoopExpression :
loopBlockExpression
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 :
whileExpressionexcept struct expression BlockExpression
A while loop begins by evaluating the boolean loop conditional expression. If
the loop conditional expression evaluates to true, the loop body block
executes, then control returns to the loop conditional expression. If the loop
conditional expression evaluates to false, the while expression completes.
An example:
#![allow(unused_variables)] fn main() { let mut i = 0; while i < 10 { println!("hello"); i = i + 1; } }
Predicate pattern loops
Syntax
PredicatePatternLoopExpression :
whileletMatchArmPatterns=Expressionexcept struct or 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_variables)] 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_variables)] 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 :
forPatterninExpressionexcept 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_variables)] 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_variables)] fn main() { let mut sum = 0; for n in 1..11 { sum += n; } assert_eq!(sum, 55); }
A for loop is equivalent to the following block expression.
'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 PAT = 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
matchis used to ensure that any temporary values initer_exprdon't get dropped before the loop is finished.nextis 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.
break expressions
Syntax
BreakExpression :
breakLIFETIME_OR_LABEL? Expression?
When break is encountered, execution of the associated loop body is
immediately terminated, for example:
#![allow(unused_variables)] 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_variables)] 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.
continue expressions
Syntax
ContinueExpression :
continueLIFETIME_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_variables)] 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 ().