循环和其他可中断的表达式

语法
LoopExpression :
   LoopLabel? (
         InfiniteLoopExpression
      | PredicateLoopExpression
      | PredicatePatternLoopExpression
      | IteratorLoopExpression
      | LabelBlockExpression
   )

Rust 支持五种循环表达式

所有五种类型的循环都支持 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 Pattern in 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
}

IntoIteratorIteratorOption 始终是标准库项,而不是当前作用域中解析为这些名称的任何项。变量名 nextiterval 仅用于说明,它们实际上没有用户可以键入的名称。

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

循环标签

语法
LoopLabel :
   LIFETIME_OR_LABEL :

循环表达式可以选择性地具有标签。标签写成在循环表达式之前的生命周期,例如 'foo: loop { break 'foo; }'bar: while false {}'humbug: for _ in 0..0 {}。如果存在标签,则嵌套在此循环中的带标签的 breakcontinue 表达式可以退出此循环或将控制权返回到其头部。请参阅 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 表达式的 loopforwhile 循环关联,但可以使用 标签 来指定受影响的包围循环。例子

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

break 表达式仅允许在循环体中使用,并且具有 breakbreak 'label 或(见下文break EXPRbreak '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 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 相同。