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
InfiniteLoopExpressionloop BlockExpression

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

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

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

谓词循环

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

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

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

Note

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

循环标签

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

Syntax
BreakExpressionbreak 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 表达式的最内层 loopforwhile 循环关联,但可以使用标签来指定受影响的封闭循环。示例:

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

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

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

标记块表达式

Syntax
LabelBlockExpressionBlockExpression

标记块表达式与块表达式完全相同,只是它们允许在块内使用 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
ContinueExpressioncontinue LIFETIME_OR_LABEL?

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

带有关联 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`
}
}