循环和其他可中断的表达式
语法
LoopExpression :
LoopLabel? (
InfiniteLoopExpression
| PredicateLoopExpression
| PredicatePatternLoopExpression
| IteratorLoopExpression
| LabelBlockExpression
)
Rust 支持五种循环表达式
- 一个
loop
表达式 表示一个无限循环。 - 一个
while
表达式 循环直到谓词为假。 - 一个
while let
表达式 测试一个模式。 - 一个
for
表达式 从一个迭代器中提取值,循环直到迭代器为空。 - 一个 带标签的代码块表达式 恰好运行一次循环,但允许使用
break
提前退出循环。
所有五种类型的循环都支持 break
表达式 和 标签。除了带标签的代码块表达式外,所有类型都支持 continue
表达式。只有 loop
和带标签的代码块表达式支持 求值为非平凡值。
无限循环
语法
InfiniteLoopExpression :
loop
BlockExpression
一个 loop
表达式持续重复执行其代码体:loop { println!("I live."); }
。
一个不带关联 break
表达式的 loop
表达式是发散的,并且具有类型 !
。一个包含关联 break
表达式 的 loop
表达式可能会终止,并且必须具有与 break
表达式的值兼容的类型。
谓词循环
语法
PredicateLoopExpression :
while
Expressionexcept struct expression BlockExpression
一个 while
循环首先计算 布尔 循环条件操作数。如果循环条件操作数求值为 true
,则执行循环体代码块,然后控制返回到循环条件操作数。如果循环条件表达式求值为 false
,则 while
表达式完成。
一个例子
#![allow(unused)] fn main() { let mut i = 0; while i < 10 { println!("hello"); i = i + 1; } }
谓词模式循环
语法
PredicatePatternLoopExpression :
while
let
Pattern=
Scrutineeexcept lazy boolean operator expression BlockExpression
一个 while let
循环在语义上类似于 while
循环,但它期望使用关键字 let
,后跟一个模式、一个 =
、一个 scrutinee 表达式和一个代码块表达式,而不是条件表达式。如果 scrutinee 的值与模式匹配,则执行循环体代码块,然后控制返回到模式匹配语句。否则,while 表达式完成。
#![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; } }
一个 while let
循环等价于一个包含 match
表达式 的 loop
表达式,如下所示。
'label: while let PATS = EXPR {
/* loop body */
}
等价于
'label: loop {
match EXPR {
PATS => { /* loop body */ },
_ => break,
}
}
可以使用 |
运算符指定多个模式。这与 match
表达式中 |
的语义相同
#![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); } }
与 if let
表达式 中的情况一样,scrutinee 不能是 惰性布尔运算符表达式。
迭代器循环
语法
IteratorLoopExpression :
for
Patternin
Expressionexcept struct expression BlockExpression
一个 for
表达式是用于循环遍历 std::iter::IntoIterator
实现提供的元素的语法结构。如果迭代器产生一个值,则该值与不可反驳的模式匹配,执行循环体,然后控制返回到 for
循环的头部。如果迭代器为空,则 for
表达式完成。
一个遍历数组内容的 for
循环的例子
#![allow(unused)] fn main() { let v = &["apples", "cake", "coffee"]; for text in v { println!("I like {}.", text); } }
一个遍历一系列整数的 for 循环的例子
#![allow(unused)] fn main() { let mut sum = 0; for n in 1..11 { sum += n; } assert_eq!(sum, 55); }
一个 for
循环等价于一个包含 match
表达式 的 loop
表达式,如下所示
'label: for PATTERN in iter_expr {
/* loop body */
}
等价于
{
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
和 Option
始终是标准库项,而不是当前作用域中解析为这些名称的任何项。变量名 next
、iter
和 val
仅用于说明,它们实际上没有用户可以键入的名称。
注意:外部
match
用于确保iter_expr
中的任何 临时值 在循环完成之前不会被丢弃。next
在赋值之前声明,因为它更经常导致类型被正确推断。
循环标签
语法
LoopLabel :
LIFETIME_OR_LABEL:
循环表达式可以选择性地具有标签。标签写成在循环表达式之前的生命周期,例如 'foo: loop { break 'foo; }
,'bar: while false {}
,'humbug: for _ in 0..0 {}
。如果存在标签,则嵌套在此循环中的带标签的 break
和 continue
表达式可以退出此循环或将控制权返回到其头部。请参阅 break 表达式 和 continue 表达式。
标签遵循局部变量的卫生和阴影规则。例如,此代码将打印 “outer loop”
#![allow(unused)] fn main() { 'a: loop { 'a: loop { break 'a; } print!("outer loop"); break 'a; } }
'_
不是有效的循环标签。
break
表达式
语法
BreakExpression :
break
LIFETIME_OR_LABEL? Expression?
当遇到 break
时,关联的循环体的执行会立即终止,例如
#![allow(unused)] fn main() { let mut last = 0; for x in 1..100 { if x > 12 { break; } last = x; } assert_eq!(last, 12); }
break
表达式通常与最内层包围 break
表达式的 loop
、for
或 while
循环关联,但可以使用 标签 来指定受影响的包围循环。例子
#![allow(unused)] fn main() { 'outer: loop { while true { break 'outer; } } }
break
表达式仅允许在循环体中使用,并且具有 break
、break 'label
或(见下文)break EXPR
或 break 'label EXPR
的形式之一。
带标签的代码块表达式
语法
LabelBlockExpression :
BlockExpression
带标签的代码块表达式与代码块表达式完全相同,只是它们允许在代码块内使用 break
表达式。与循环不同,带标签的代码块表达式内的 break
表达式必须具有标签(即,标签不是可选的)。同样,带标签的代码块表达式必须以标签开头。
#![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
表达式
语法
ContinueExpression :
continue
LIFETIME_OR_LABEL?
当遇到 continue
时,关联的循环体的当前迭代会立即终止,并将控制权返回到循环头部。在 while
循环的情况下,头部是控制循环的条件表达式。在 for
循环的情况下,头部是控制循环的调用表达式。
与 break
类似,continue
通常与最内层包围循环关联,但可以使用 continue 'label
来指定受影响的循环。continue
表达式仅允许在循环体中使用。
break
和循环值
当与 loop
关联时,break 表达式可以用于从该循环返回值,通过 break EXPR
或 break 'label EXPR
的形式之一,其中 EXPR
是一个表达式,其结果从 loop
返回。例如
#![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); }
在 loop
具有关联的 break
的情况下,它不被认为是发散的,并且 loop
必须具有与每个 break
表达式兼容的类型。没有表达式的 break
被认为与带有表达式 ()
的 break
相同。