循环及其他可中断表达式

语法
LoopExpression :
   循环标签? (
         无限循环表达式
      | 谓词循环表达式
      | 谓词模式循环表达式
      | 迭代器循环表达式
      | 带标签块表达式
   )

Rust 支持五种循环表达式

所有五种类型的循环都支持 break 表达式标签

除带标签块表达式外,所有类型都支持 continue 表达式

只有 loop 和带标签块表达式支持评估为非平凡值

无限循环

语法
无限循环表达式 :
   loop 块表达式

一个 loop 表达式连续重复执行其主体:loop { println!("I live."); }

一个没有关联的 break 表达式的 loop 表达式是发散的(diverging),其类型为 !

包含关联的break 表达式loop 表达式可能会终止,其类型必须与 break 表达式的值兼容。

谓词循环

语法
谓词循环表达式 :
   while 表达式结构体表达式除外 块表达式

一个 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 循环,但它不是条件表达式,而是期望关键字 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 模式 in 表达式结构体表达式除外 块表达式

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
}

此处的 IntoIteratorIteratorOption 始终是标准库项,而不是这些名称在当前作用域中解析到的任何东西。

变量名 nextiterval 仅用于说明目的,用户实际上无法键入这些名称。

注意

外部的 match 用于确保 iter_expr 中的任何临时值在循环完成之前不会被丢弃。next 在赋值之前声明,因为它通常会使类型推断更正确。

循环标签

语法
循环标签 :
   生命周期或标签 :

循环表达式可以可选地拥有一个标签。标签写在循环表达式之前,形式为一个生命周期,例如 'foo: loop { break 'foo; }, 'bar: while false {}, 'humbug: for _ in 0..0 {}

如果存在标签,则嵌套在此循环内的带标签的 breakcontinue 表达式可以退出此循环或将控制权返回到其头部。参见break 表达式continue 表达式

标签遵循局部变量的卫生(hygiene)和遮蔽(shadowing)规则。例如,此代码将打印“outer loop”

#![allow(unused)]
fn main() {
'a: loop {
    'a: loop {
        break 'a;
    }
    print!("outer loop");
    break 'a;
}
}

'_ 不是有效的循环标签。

break 表达式

语法
Break 表达式 :
   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 表达式的最内层 loopforwhile 循环相关联,但可以使用标签来指定影响哪个外层循环。示例

#![allow(unused)]
fn main() {
'outer: loop {
    while true {
        break 'outer;
    }
}
}

break 表达式只允许在循环体中使用,其形式可以是 breakbreak 'label,或者(见下文break EXPRbreak '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 EXPRbreak '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