循环及其他可中断表达式
语法
LoopExpression :
循环标签? (
无限循环表达式
| 谓词循环表达式
| 谓词模式循环表达式
| 迭代器循环表达式
| 带标签块表达式
)
Rust 支持五种循环表达式
- 一个
loop表达式表示一个无限循环。 - 一个
while表达式循环直到谓词为假。 - 一个
while let表达式测试一个模式。 - 一个
for表达式从一个迭代器中提取值,循环直到迭代器为空。 - 一个 带标签块表达式精确地运行一次循环,但允许使用
break提前退出循环。
除带标签块表达式外,所有类型都支持 continue 表达式。
只有 loop 和带标签块表达式支持评估为非平凡值。
无限循环
语法
无限循环表达式 :
loop块表达式
一个 loop 表达式连续重复执行其主体:loop { println!("I live."); }。
一个没有关联的 break 表达式的 loop 表达式是发散的(diverging),其类型为 !。
包含关联的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 实现提供的元素。
如果迭代器产生一个值,该值将与不可驳斥模式(irrefutable pattern)匹配,然后执行循环体,之后控制返回到 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 表达式。
标签遵循局部变量的卫生(hygiene)和遮蔽(shadowing)规则。例如,此代码将打印“outer loop”
#![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。