|
|
|
@ -2,7 +2,7 @@
|
|
|
|
|
|
|
|
|
|
80后应该都对学校的小混混记忆犹新,在那个时代,小混混们往往都认为自己是地下王者,管控着地下事务的流程,在我看来,他们就像代码中的流程控制一样,无处不在,很显眼,但是又让人懒得重视。
|
|
|
|
|
|
|
|
|
|
言归正传,Rust程序是从上而下顺序执行的,在此过程中,我们可以引入循环、分支等流程控制方式,帮助我们的代码更好的实现相应的功能。
|
|
|
|
|
言归正传,Rust程序是从上而下顺序执行的,在此过程中,我们可以通过循环、分支等流程控制方式,更好的实现相应的功能。
|
|
|
|
|
|
|
|
|
|
## 使用if来做分支控制
|
|
|
|
|
> if else无处不在 - `鲁迅说`
|
|
|
|
@ -16,7 +16,7 @@ if condition == true {
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
该代码读作:若`condition`条件为`true`,则执行`A`代码,否则执行`B`代码.
|
|
|
|
|
该代码读作:若`condition`条件为`true`,则执行`A`代码,否则执行`B`代码。
|
|
|
|
|
|
|
|
|
|
先看下面代码:
|
|
|
|
|
```rust
|
|
|
|
@ -33,7 +33,7 @@ fn main() {
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
以上代码有以下几点要注意:
|
|
|
|
|
- **`if`语句块是表达式**, 这里我们使用`if`表达式的返回值来给`number`进行赋值: `number`的值是`5`。
|
|
|
|
|
- **`if`语句块是表达式**,这里我们使用`if`表达式的返回值来给`number`进行赋值:`number`的值是`5`。
|
|
|
|
|
- 用`if`来赋值时,要保证每个分支返回的类型一样(事实上,这种说法不完全准确,见[这里](../appendix/expressions.md#if表达式)),此处返回的`5`和`6`就是同一个类型,如果返回类型不一致就会报错
|
|
|
|
|
|
|
|
|
|
```console
|
|
|
|
@ -53,7 +53,7 @@ error[E0308]: if and else have incompatible types
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 使用else if来处理多重条件
|
|
|
|
|
可以将`else if`与`if`、`else`组合在一起实现多种条件分支判断:
|
|
|
|
|
可以将`else if`与`if`、`else`组合在一起实现更复杂的条件分支判断:
|
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
let n = 6;
|
|
|
|
@ -77,13 +77,13 @@ fn main() {
|
|
|
|
|
|
|
|
|
|
## 循环控制
|
|
|
|
|
|
|
|
|
|
循环无处不在,上到数钱,下到数年,你能想象的很多场景都存在循环,因此它也是流程控制中最重要的组成部分之一.
|
|
|
|
|
循环无处不在,上到数钱,下到数年,你能想象的很多场景都存在循环,因此它也是流程控制中最重要的组成部分之一。
|
|
|
|
|
|
|
|
|
|
在Rust语言中有三种循环方式:`for`、`while`和`loop`,其中`for`循环是Rust循环王冠上的明珠。
|
|
|
|
|
|
|
|
|
|
#### for循环
|
|
|
|
|
|
|
|
|
|
`for`循环是Rust的大杀器:
|
|
|
|
|
`for`循环是Rust的大杀器:
|
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
for i in 1..=5 {
|
|
|
|
@ -92,7 +92,7 @@ fn main() {
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
以上代码循环输出一个从1到5的序列,简单粗暴,核心就在于`for`和`in`的联动,语义表达如下:
|
|
|
|
|
以上代码循环输出一个从1到5的序列,简单粗暴,核心就在于`for`和`in`的联动,语义表达如下:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
for 元素 in 集合 {
|
|
|
|
@ -101,14 +101,14 @@ for 元素 in 集合 {
|
|
|
|
|
```
|
|
|
|
|
这个语法跟`javascript`还蛮像,应该挺好理解。
|
|
|
|
|
|
|
|
|
|
注意,使用`for`我们往往使用集合的引用形式,除非你不想在后面的代码中继续使用该集合(不使用引用的话,所有权会被转移到`for`语句块中):
|
|
|
|
|
注意,使用`for`时我们往往使用集合的引用形式,除非你不想在后面的代码中继续使用该集合(比如我们这里使用了container的引用)。如果不使用引用的话,所有权会被转移到`for`语句块中,后面就无法再使用这个集合了):
|
|
|
|
|
```rust
|
|
|
|
|
for item in &container {
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
如果想在循环中,**修改该元素**,使用`mut`关键字:
|
|
|
|
|
如果想在循环中,**修改该元素**,可以使用`mut`关键字:
|
|
|
|
|
```rust
|
|
|
|
|
for item in &mut collection {
|
|
|
|
|
// ...
|
|
|
|
@ -134,13 +134,13 @@ fn main() {
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
有同学可能会想到,如果我们要用`for`循环控制某个过程执行10次,但是又不关心那个计数值,该怎么写?
|
|
|
|
|
有同学可能会想到,如果我们想用`for`循环控制某个过程执行10次,但是又不想单独声明一个变量来控制这个流程,该怎么写?
|
|
|
|
|
```rust
|
|
|
|
|
for _ in 0..10 {
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
可以用`_`来替代`i`用于`for`循环中,在Rust中`_`的含义是忽略该值或者类型的意思,如果不使用`_`,那么编译器会给你一个`变量未使用的`的警告.
|
|
|
|
|
可以用`_`来替代`i`用于`for`循环中,在Rust中`_`的含义是忽略该值或者类型的意思,如果不使用`_`,那么编译器会给你一个`变量未使用的`的警告。
|
|
|
|
|
|
|
|
|
|
**两种循环方式优劣对比**
|
|
|
|
|
|
|
|
|
@ -160,13 +160,13 @@ for item in collection {
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
第一种方式是循环索引,然后通过索引下标去访问集合,第二种方式是直接循环集合中的元素,优劣如下:
|
|
|
|
|
- **性能**:第一种使用方式中`collection[index]`的索引访问,会因为边界检查(bounds checking)导致运行时的性能损耗 - Rust会检查并确认`index`是落在集合内,但是第二种直接迭代的方式就不会触发这种检查,因为编译器会在编译时就完成分析并证明这种访问是合法的
|
|
|
|
|
- **安全**: 第一种方式里对`collection`的索引访问是非连续的,存在一定可能性在两次访问之间,`collection`发生了变化,导致脏数据产生。而第二种直接迭代的方式是连续访问,因此不存在这种风险
|
|
|
|
|
- **性能**:第一种使用方式中`collection[index]`的索引访问,会因为边界检查(bounds checking)导致运行时的性能损耗 - Rust会检查并确认`index`是落在集合内,但是第二种直接迭代的方式就不会触发这种检查,因为编译器会在编译时就完成分析并证明这种访问是合法的
|
|
|
|
|
- **安全**:第一种方式里对`collection`的索引访问是非连续的,存在一定可能性在两次访问之间,`collection`发生了变化,导致脏数据产生。而第二种直接迭代的方式是连续访问,因此不存在这种风险(这里是因为所有权吗?是的话可能要强调一下)
|
|
|
|
|
|
|
|
|
|
由于for循环无需任何条件限制,也不需要通过索引来访问,因此是最安全也是最常用的,在下面的`while`中,我们能看到为什么`for`会更加安全。
|
|
|
|
|
由于for循环无需任何条件限制,也不需要通过索引来访问,因此是最安全也是最常用的,通过与下面的`while`的对比,我们能看到为什么`for`会更加安全。
|
|
|
|
|
|
|
|
|
|
#### `continue`
|
|
|
|
|
使用`continue`可以跳过当前当次的循环,开始下次的循环:
|
|
|
|
|
使用`continue`可以跳过当前当次的循环,开始下次的循环:
|
|
|
|
|
```rust
|
|
|
|
|
for i in 1..4 {
|
|
|
|
|
if i == 2 {
|
|
|
|
@ -198,7 +198,7 @@ fn main() {
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
该`while`循环,只有当`n`小于等于`5`时,才执行,否则就立刻跳出循环,因此在上述代码中,它会先从`0`开始,满足条件,进行循环,然后是`1`,满足条件,进行循环,最终到`6`的时候,不满足条件,跳出`while`循环,执行`我出来了`的打印,然后程序结束:
|
|
|
|
|
该`while`循环,只有当`n`小于等于`5`时,才执行,否则就立刻跳出循环,因此在上述代码中,它会先从`0`开始,满足条件,进行循环,然后是`1`,满足条件,进行循环,最终到`6`的时候,大于5,不满足条件,跳出`while`循环,执行`我出来了`的打印,然后程序结束:
|
|
|
|
|
```console
|
|
|
|
|
0!
|
|
|
|
|
1!
|
|
|
|
@ -209,7 +209,7 @@ fn main() {
|
|
|
|
|
我出来了!
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
当然,你也可以用其它方式组合实现,例如`loop`(无条件循环,将在下面介绍) + `if` + `break`:
|
|
|
|
|
当然,你也可以用其它方式组合实现,例如`loop`(无条件循环,将在下面介绍) + `if` + `break`:
|
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
let mut n = 0;
|
|
|
|
@ -311,6 +311,6 @@ fn main() {
|
|
|
|
|
以上代码当`counter`递增到`10`时,就会通过`break`返回一个`counter*2`的值,最后赋给`result`并打印出来。
|
|
|
|
|
|
|
|
|
|
这里有几点值得注意:
|
|
|
|
|
- **break可以单独使用,也可以带一个返回值**,有些类似`return`
|
|
|
|
|
- **break可以单独使用,也可以带一个返回值**,有些类似`return`
|
|
|
|
|
- **loop是一个表达式**,因此可以返回一个值
|
|
|
|
|
|
|
|
|
|