Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

循环和其他可中断表达式

Syntax
LoopExpression →
    LoopLabel? (
        InfiniteLoopExpression
      | PredicateLoopExpression
      | IteratorLoopExpression
      | LabelBlockExpression
    )

Rust 支持四种循环表达式:

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

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

只有 loop 和标记块表达式支持求值为非平凡值。

无限循环

Syntax
InfiniteLoopExpression → loop BlockExpression

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

没有关联 break 表达式的 loop 表达式是发散的,类型为 !。

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

谓词循环

Syntax
PredicateLoopExpression → while Conditions BlockExpression

while 循环表达式允许在一组条件保持为 true 时重复求值块。

条件操作数必须是具有布尔类型的 Expression 或条件 let 匹配。如果所有条件操作数都求值为 true 并且所有 let 模式都成功匹配其审查者,则执行循环主体块。

循环主体成功执行后,重新求值条件操作数以确定是否应再次执行主体。

如果任何条件操作数求值为 false 或任何 let 模式未匹配其审查者,则不执行主体,执行在 while 表达式之后继续。

while 表达式求值为 ()。

示例:

#![allow(unused)]
fn main() {
let mut i = 0;

while i < 10 {
    println!("hello");
    i = i + 1;
}
}

while let 模式

while 条件中的 let 模式允许在模式成功匹配时将新变量绑定到作用域中。以下示例说明了使用 let 模式的绑定:

#![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);
}
}

while 条件链

多个条件操作数可以用 && 分隔。这些与 if 条件链具有相同的语义和限制。

以下是链接多个表达式、混合 let 绑定和布尔表达式、以及表达式能够引用先前表达式的模式绑定的示例:

fn main() {
    let outer_opt = Some(Some(1i32));

    while let Some(inner_opt) = outer_opt
        && let Some(number) = inner_opt
        && number == 1
    {
        println!("Peek a boo");
        break;
    }
}

迭代器循环

Syntax
IteratorLoopExpression →
    for Pattern in Expressionexcept StructExpression 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
}

IntoIterator、Iterator 和 Option 始终是此处的标准库项,而不是这些名称在当前作用域中解析的任何内容。

变量名称 next、iter 和 val 仅用于说明,它们实际上没有用户可以输入的名称。

Note

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

循环标签

Syntax
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 表达式

Syntax
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 表达式通常与包含 break 表达式的最内层 loop、for 或 while 循环关联,但可以使用标签来指定受影响的封闭循环。示例:

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

break 表达式只允许在循环主体中,具有 break、break 'label 或(见下文)break EXPR 或 break 'label EXPR 形式之一。

在带 break 表达式的 loop 或标记块表达式中,没有表达式的 break 等同于 break ()。

标记块表达式

Syntax
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
};
}

标记块表达式的类型是所有 break 操作数和最终操作数的最小上界。如果省略最终操作数,则最终操作数的类型默认为单元类型,除非块发散,在这种情况下它是永不类型。

Example

#![allow(unused)]
fn main() {
fn example(condition: bool) {
    let s = String::from("owned");

    let _: &str = 'block: {
        if condition {
            break 'block &s;  // &String coerced to &str via Deref
        }
        break 'block "literal";  // &'static str coerced to &str
    };
}
}

continue 表达式

Syntax
ContinueExpression → continue LIFETIME_OR_LABEL?

遇到 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);
}

带有关联 break 表达式的 loop 的类型是所有 break 操作数的最小上界。

Example

#![allow(unused)]
fn main() {
fn example(condition: bool) {
    let s = String::from("owned");

    let _: &str = loop {
        if condition {
            break &s; // &String coerced to &str via Deref
        }
        break "literal"; // &'static str coerced to &str
    };
}
}

如果任何 break 操作数不发散,则带有关联 break 表达式的 loop 不发散。如果所有 break 操作数都发散,则 loop 表达式也发散。

Example

#![allow(unused)]
fn main() {
fn diverging_loop_with_break(condition: bool) -> ! {
    // This loop is diverging because all `break` operands are diverging.
    loop {
        if condition {
            break loop {};
        } else {
            break panic!();
        }
    }
}
}
#![allow(unused)]
fn main() {
fn loop_with_non_diverging_break(condition: bool) -> ! {
    // The type of this loop is i32 even though one of the breaks is
    // diverging.
    loop {
        if condition {
            break loop {};
        } else {
            break 123i32;
        }
    } // ERROR: expected `!`, found `i32`
}
}