循环和其他可中断的表达式
语法
循环表达式 :
循环标签? (
无限循环表达式
| 断言循环表达式
| 断言模式循环表达式
| 迭代循环表达式
| 标签块表达式
)
Rust 支持五种循环表达式:
loop
表达式 表示无限循环。while
表达式 循环直到断言为 false。while let
表达式 测试模式。for
表达式 从迭代器中提取值,循环直到迭代器为空。- 带标签的块表达式 只运行一次循环,但允许使用
break
提前退出循环。
这五种类型的循环都支持 break
表达式 和 标签 。
除了带标签的块表达式外,所有类型的循环都支持 continue
表达式 。
只有 loop
和带标签的块表达式支持 计算非平凡值 。
无限循环
语法
无限循环表达式 :
loop
块表达式
loop
表达式不断重复执行其主体:
loop { println!("I live."); }
。
没有相关的 break
表达式的 loop
表达式是发散的,并且具有 !
类型。
包含相关 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
的实现提供的元素。
如果迭代器产生一个值,那么该值将与不可拒绝的模式匹配,执行循环体,然后控制返回到 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 表达式 。
标签遵循局部变量的卫生和隐藏规则。例如,以下代码将打印 "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
表达式通常与包含它的最内层 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
和循环值
在 Rust 中,当与 loop
关键字相关联时,可以使用 break
表达式通过 break EXPR
或 break 'label EXPR
的形式从循环中返回一个值,其中 EXPR
是返回给 loop
的表达式结果。
例如:
#![allow(unused)] fn main() { let (mut a, mut b) = (1, 1); // 初始化 a 和 b 为 1 let result = loop { // 进入循环 if b > 10 { // 如果 b 大于 10 break b; // 跳出循环并返回 b 的值 } let c = a + b; a = b; // 将 a 赋值为上一个 b 的值 b = c; // 将 b 赋值为上一个 c 的值 }; // 斐波那契数列中第一个大于 10 的数是 13 : assert_eq!(result, 13); }
在 loop
语句中,如果有关联的 break
语句,则不被视为发散,而且 loop
语句必须与每个 break
表达式兼容的类型。
未表明表达式的 break
语句的表达式被视为 ()
。