Loops and other breakable expressions
循环和其他可中断表达式
loop-expr.md
commit: aa9c70bda63b3ab73b15746609831dafb96f56ff
本章译文最后维护日期:2023-05-03
句法
LoopExpression :
LoopLabel? (
InfiniteLoopExpression
| PredicateLoopExpression
| PredicatePatternLoopExpression
| IteratorLoopExpression
| LabelBlockExpression
)
Rust支持五种循环表达式:
loop
表达式表示一个无限循环。while
表达式不断循环,直到谓词为假。while let
表达式循环测试给定模式。for
表达式从迭代器中循环取值,直到迭代器为空。- 带标签的块表达式 的循环一旦启动,将一直循环,但可以通过使用
break
来提前退出循环。
所有五种类型的循环都支持 break
表达式和循环标签(label).
除了带标签的块表达式都支持 continue
表达式。
只有 loop
循环和带标签的块表达式支持非平凡求值(evaluation to non-trivial values)1。
Infinite loops
无限循环
句法
InfiniteLoopExpression :
loop
BlockExpression
loop
表达式会不断地重复地执行它代码体内的代码:loop { println!("I live."); }
。
没有包含关联的 break
表达式的 loop
表达式是发散的,并且具有类型 !
。
包含相应 break
表达式的 loop
表达式可以结束循环,并且此表达式的类型必须与 break
表达式的类型兼容。
Predicate loops
谓词循环
句法
PredicateLoopExpression :
while
Expression排除结构体表达式 BlockExpression
while
循环从对布尔型的循环条件操作数求值开始。
如果循环条件操作数的求值结果为 true
,则执行循环体块,然后控制流返回到循环条件操作数。如果循环条件操作数的求值结果为 false
,则 while
表达式完成。
举个例子:
#![allow(unused)] fn main() { let mut i = 0; while i < 10 { println!("hello"); i = i + 1; } }
Predicate pattern loops
谓词模式循环
句法
PredicatePatternLoopExpression :
while
let
Pattern=
Scrutinee排除惰性布尔运算符表达式 BlockExpression
while let
循环在语义上类似于 while
循环,但它用 let
关键字后紧跟着一个模式、一个 =
、一个检验对象(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!("不可反驳模式总是会匹配成功"); 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() { // 打印 2, 2, 然后 1 println!("{}", v); } }
与 if let
表达式的情况一样,检验表达式不能是一个懒惰布尔运算符表达式。
Iterator loops
迭代器循环
句法
IteratorLoopExpression :
for
Patternin
Expression排除结构体表达式 BlockExpression
for
表达式是一个用于在 std::iter::IntoIterator
的某个迭代器实现提供的元素上进行循环的语法结构。
如果迭代器生成一个值,该值将与此 for
表达式提供的不可反驳型模式进行匹配,执行循环体,然后控制流返回到 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
是标准库的程序项(standard library item),不是当前作用域中解析的的任何名称。
变量名 next
、iter
和 val
也仅用于表述需要,实际上它们不是用户可以输入的名称。
注意:上面代码里使用外层
matche
来确保iter_expr
中的任何临时值在循环结束前不会被销毁。next
先声明后赋值是因为这样能让编译器更准确地推断出类型。
Loop labels
循环标签
句法
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
expressions
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
循环相关联,但是可以使用循环标签来指定受影响的循环层(此循环层必须是封闭该 break表达式的循环之一)。
例如:
#![allow(unused)] fn main() { 'outer: loop { while true { break 'outer; } } }
break
表达式只允许在循环体内使用,它有 break
、break 'label
或(参见后面)break EXPR
或 break 'label EXPR
这四种形式。
Labelled block expressions
带标签的块表达式
句法
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
expressions
continue
表达式
句法
ContinueExpression :
continue
LIFETIME_OR_LABEL?
当遇到 continue
时,相关的循环体的当前迭代将立即结束,并将控制流返回到循环头。
在 while
循环的情况下,循环头是控制循环的条件表达式。
在 for
循环的情况下,循环头是控制循环的调用表达式。
与 break
一样,continue
通常与最内层的循环相关联,但可以使用 continue 'label
来指定受影响的循环层。
continue
表达式只允许在循环体内部使用。
break
and loop values
break
和loop
返回值
当使用 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; }; // 斐波那契数列中第一个大于10的值: assert_eq!(result, 13); }
如果 loop
有关联的 break
,则不认为该循环是发散的,并且 loop
表达式的类型必须与每个 break
表达式的类型兼容。
其后不跟表达式的 break
被认为与后跟 ()
的break
表达式的效果相同。
求得 ()
类型以外的值。