循环和其他可中断表达式
语法
循环表达式 :
循环标签? (
无限循环表达式
| 谓词循环表达式
| 谓词模式循环表达式
| 迭代器循环表达式
| 带标签的块表达式
)
Rust 支持五种循环表达式
loop
表达式表示无限循环。while
表达式循环直到谓词为假。while let
表达式测试一个模式。for
表达式从迭代器中提取值,循环直到迭代器为空。- 带标签的块表达式只运行一次循环,但允许使用
break
提前退出循环。
所有五种类型的循环都支持 break
表达式和 标签。除带标签的块表达式外,所有表达式都支持 continue
表达式。只有 loop
和带标签的块表达式支持 对非平凡值的求值。
无限循环
语法
无限循环表达式 :
loop
块表达式
loop
表达式连续重复执行其主体:loop { println!("我还活着。"); }
。
没有关联 break
表达式的 loop
表达式是发散的,其类型为 !
。包含关联 break
表达式的 loop
表达式可能会终止,并且必须具有与 break
表达式的值兼容的类型。
谓词循环
while
循环首先计算 布尔循环条件操作数。如果循环条件操作数计算结果为 true
,则循环体块执行,然后控制权返回到循环条件操作数。如果循环条件表达式计算结果为 false
,则 while
表达式完成。
一个例子
#![allow(unused)] fn main() { let mut i = 0; while i < 10 { println!("hello"); i = i + 1; } }
谓词模式循环
while let
循环在语义上类似于 while
循环,但在条件表达式的位置,它需要关键字 let
,后跟一个模式、一个 =
、一个 被匹配值表达式和一个块表达式。如果被匹配值的值与模式匹配,则循环体块执行,然后控制权返回到模式匹配语句。否则,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
表达式的情况一样,被匹配值不能是 惰性布尔运算符表达式。
迭代器循环
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
在赋值之前声明,因为它会导致更频繁地正确推断类型。
循环标签
语法
循环标签 :
生命周期或标签:
循环表达式可以选择有一个标签。标签写成循环表达式前面的生命周期,如 'foo: loop { break 'foo; }
、'bar: while false {}
、'humbug: for _ in 0..0 {}
。如果存在标签,则嵌套在此循环中的带标签的 break
和 continue
表达式可以退出此循环或将控制权返回到其头部。请参阅 break 表达式 和 continue 表达式。
标签遵循局部变量的卫生和遮蔽规则。例如,此代码将打印“外部循环”
#![allow(unused)] fn main() { 'a: loop { 'a: loop { break 'a; } print!("outer loop"); break 'a; } }
break
表达式
遇到 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
中的一种形式。
带标签的代码块表达式
语法
带标签的代码块表达式 :
代码块表达式
带标签的代码块表达式与代码块表达式完全相同,只是它们允许在代码块中使用 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
表达式
语法
Continue 表达式 :
continue
生命周期或标签?
遇到 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
相同。