Loops and other breakable expressions
Syntax
LoopExpression →
LoopLabel? (
InfiniteLoopExpression
|PredicateLoopExpression
|IteratorLoopExpression
|LabelBlockExpression
)
Rust supports four loop expressions:
- A
loopexpression denotes an infinite loop. - A
whileexpression loops until a predicate is false. - A
forexpression extracts values from an iterator, looping until the iterator is empty. - Alabelled block expression runs a loop exactly once, but allows exiting the loop early with
break.
All four types of loop supportbreak expressions, andlabels.
All except labelled block expressions supportcontinue expressions.
Onlyloop and labelled block expressions supportevaluation to non-trivial values.
Infinite loops
Syntax
InfiniteLoopExpression →loopBlockExpression
Aloop expression repeats execution of its body continuously:loop { println!("I live."); }.
Aloop expression without an associatedbreak expression is diverging and has type!.
Aloop expression containing associatedbreak expression(s) may terminate, and must have type compatible with the value of thebreak expression(s).
Predicate loops
Syntax
PredicateLoopExpression →whileConditionsBlockExpression
Awhile loop expression allows repeating the evaluation of a block while a set of conditions remain true.
The syntax of awhile expression is a sequence of one or more condition operands separated by&&,followed by aBlockExpression.
Condition operands must be either anExpression with aboolean type or a conditionallet match.If all of the condition operands evaluate totrue and all of thelet patterns successfully match theirscrutinees,then the loop body block executes.
After the loop body successfully executes, the condition operands are re-evaluated to determine if the body should be executed again.
If any condition operand evaluates tofalse or anylet pattern does not match its scrutinee,the body is not executed and execution continues after thewhile expression.
Awhile expression evaluates to().
An example:
#![allow(unused)]fn main() {let mut i = 0;while i < 10 { println!("hello"); i = i + 1;}}
while let patterns
let patterns in awhile condition allow binding new variables into scope when the pattern matches successfully.The following examples illustrate bindings usinglet patterns:
#![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;}}
Awhile let loop is equivalent to aloop expression containing amatch 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| inmatch 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);}}
while condition chains
Multiple condition operands can be separated with&&.These have the same semantics and restrictions asif condition chains.
The following is an example of chaining multiple expressions, mixinglet bindings and boolean expressions, and with expressions able to reference pattern bindings from previous expressions:
fn main() { let outer_opt = Some(Some(1i32)); while let Some(inner_opt) = outer_opt && let Some(number) = inner_opt && number == 1 { println!("Peek a boo"); break; }}
Iterator loops
Syntax
IteratorLoopExpression →
forPatterninExpressionexceptStructExpressionBlockExpression
Afor expression is a syntactic construct for looping over elements provided by an implementation ofstd::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 thefor loop.If the iterator is empty, thefor expression completes.
An example of afor 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);}
Afor loop is equivalent to aloop expression containing amatch 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, andOption are always the standard library items here, not whatever those names resolve to in the current scope.
The variable namesnext,iter, andval are for exposition only, they do not actually have names the user can type.
Note
The outer
matchis used to ensure that anytemporary 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 alabel. 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 labeledbreak andcontinue expressions nested within this loop may exit out of this loop or return control to its head.Seebreak expressions andcontinue 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;}}
'_ is not a valid loop label.
break expressions
Syntax
BreakExpression →breakLIFETIME_OR_LABEL?Expression?
Whenbreak 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);}
Abreak expression is normally associated with the innermostloop,for orwhile loop enclosing thebreak expression,but alabel can be used to specify which enclosing loop is affected.Example:
#![allow(unused)]fn main() {'outer: loop { while true { break 'outer; }}}
Abreak expression is only permitted in the body of a loop, and has one of the formsbreak,break 'label or (see below)break EXPR orbreak 'label EXPR.
Labelled block expressions
Labelled block expressions are exactly like block expressions, except that they allow usingbreak expressions within the block.
Unlike loops,break expressions within a labelled block expressionmust have a label (i.e. the label is not optional).
Similarly, labelled block expressionsmust 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 →continueLIFETIME_OR_LABEL?
Whencontinue is encountered, the current iteration of the associated loop body is immediately terminated, returning control to the loophead.
In the case of awhile loop, the head is the conditional operands controlling the loop.
In the case of afor loop, the head is the call-expression controlling the loop.
Likebreak,continue is normally associated with the innermost enclosing loop, butcontinue 'label may be used to specify the loop affected.
Acontinue expression is only permitted in the body of a loop.
break and loop values
When associated with aloop, a break expression may be used to return a value from that loop, via one of the formsbreak EXPR orbreak 'label EXPR, whereEXPR is an expression whose result is returned from theloop.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 aloop has an associatedbreak, it is not considered diverging, and theloop must have a type compatible with eachbreak expression.break without an expression is considered identical tobreak with expression().