1. 条件结构

Rust 中主要的条件结构为 if 表达式。

1.1 if 表达式

if 表达式的基本语法格式如下:

1
2
3
4
5
6
7
8
9
if <condition1> {
...
} else if <condition2> {
...
} else if <conditionn> {
...
} else {
...
}

其中,else ifelse 分支都是可选的。表达式中的条件 conditionX 必须是 bool 类型的。

  • 由于是表达式,因此可以将 if 表达式的值赋值给变量。
  • 由于 Rust 是静态编译语言,要求在编译时就确定所有变量的类型,因此 if 表达式的每个分支可能的返回值都必须相同。
1
2
3
4
5
6
let condition = true;
let branch = if condition {
1
} else {
2
}

2. 循环结构

Rust 中提供的循环结构有 loopwhilefor。Rust 提供 break 表达式用于退出循环,并可选地返回一个返回值。

2.1 loop

loop 结构表示一直执行循环体,直到遇到 break 表达式。

1
2
3
4
5
6
7
8
9
10
11
let mut counter = 0;

let result = loop {
counter += 1;

if counter == 10 {
break counter * 2;
}
};

println!("The result is {}", result);

2.2 while

while 结构中,while 关键字后接一个条件表达式用于判断循环是否继续,从而实现条件循环。当然,while 结构可以通过组合 loopifelsebreak 来实现。

1
2
3
4
5
6
7
8
9
let mut number = 3;

while number != 0 {
println!("{}", number);

number = number - 1;
}

println!("LIFTOFF!!!");

2.3 for

for 结构可以用来遍历集合中的元素和范围空间。虽然可以通过 while 和索引值判断来实现 for 模式,但这个过程很容易出错,也使得程序更慢,因为编译器增加了运行时代码来对每次循环的每个元素进行条件检查。

1
2
3
4
5
6
7
8
9
10
11
12
let a = [10, 20, 30, 40, 50];

// 遍历元素集合
for element in a.iter() {
println!("The value is: {}", element);
}

// 遍历范围区间
// 使用了标准库提供的 Range 类型
for number in (1..4).rev() { // rev 方法用于反转 range
println!("The value is: {}", number);
}

【注】 for 循环的安全性和简洁性使得它成为 Rust 中使用最多的循环结构。

3. 匹配结构

Rust 有一个叫做 match 的极为强大的控制流运算符,它允许我们将一个值与一系列的模式相比较,并根据相匹配的模式执行相应代码。模式可由字面值、变量、通配符和许多其他内容构成。一个 match 匹配示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}

fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
  • match 的一个分支有两个部分:一个模式和一些代码,二者使用 => 分隔。每个分支之间使用 , 分隔。
  • match 表达式执行时,它将结果值按顺序与每一个分支的模式相比较。如果模式匹配了这个值,这个模式相关联的代码将被执行。如果模式并不匹配这个值,将继续执行下一个分支。
  • match 分支可以绑定匹配模式的部分值,即从枚举成员中提取值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
enum UsState {
Alabama,
Alaska,
// --snip--
}

enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}

fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter(state) => {
println!("State quarter from {:?}!", state);
25
},
}
}
  • match 必须覆盖所有可能的模式匹配分支,即所谓「穷尽性检查」。不过可以使用 _ 通配符来实现列举其余未列举出的分支。由于 _ 通配符会匹配所有的值,所以需要将其放在最后一个分支。
1
2
3
4
5
6
7
8
let some_u8_value = 0u8;
match some_u8_value {
1 => println!("one"),
3 => println!("three"),
5 => println!("five"),
7 => println!("seven"),
_ => (),
}

if let 简单控制流

可以看到,match 对于处理只有很少甚至一个分支的情况就比较啰嗦了。另一个简单的控制流 if let 语法让我们以一种不那么冗长的方式结合 iflet,来处理只匹配一个模式的值而忽略其他模式的情况。

  • 匹配一个模式举例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// match 实现
let some_u8_value = Some(0u8);
match some_u8_value {
Some(3) => println!("three"),
_ => (),
}
// if let 实现单模式分支匹配
if let Some(3) = some_u8_value {
println!("three");
}
// if let 实现 match 匹配
if let Some(3) = some_u8_value {
println!("three");
} else {
()
}
  • if let 获取通过等号分隔的一个模式和一个表达式,它的工作方式与 match 相同。
  • if let 可以使代码更简洁,但会丢失 match 强制要求的穷尽性检查。