修复一些文字错误

pull/100/head
sunface 3 years ago
parent 628b2e6f9b
commit c0f0ed5548

@ -1 +1,7 @@
# advance rust # Rust高级进阶
恭喜你学会Rust基础后金丹大道已在向你招手大部分Rust代码对你来说都是家常便饭简单的很。可是对于一门难度传言在外的语言怎么可能如此简单的就被征服最难的生命周期咱还没见过长啥样呢。
从本章开始我们将进入Rust的进阶学习环节与基础环节不同的是由于你已经对Rust有了一定的认识因此我们**不会再对很多细节进行翻来覆去的详细讲解,甚至会一带而过**。
总之欢迎来到高级Rust的世界全新的Boss全新的装备你准备好了吗

@ -29,7 +29,11 @@ fn main() {
```rust ```rust
let mut v = 0; let mut v = 0;
for i in 1..10 { for i in 1..10 {
v = if i == 9 { continue } else { i } v = if i == 9 {
continue
} else {
i
}
} }
println!("{}",v); println!("{}",v);
``` ```

@ -7,7 +7,7 @@
## 使用if来做分支控制 ## 使用if来做分支控制
> if else无处不在 - `鲁迅说` > if else无处不在 - `鲁迅说`
但凡你能找到一门编程语言没有`if else`,那么一定更要反馈给鲁迅,反正不是我说的:) 总之,只要你拥有其它语言的编程经验,就一定会有以下认知:`if else`**表达式**允许根据条件执行不同的代码分支: 但凡你能找到一门编程语言没有`if else`,那么一定更要反馈给鲁迅,反正不是我说的:) 总之,只要你拥有其它语言的编程经验,就一定会有以下认知:`if else`**表达式**根据条件执行不同的代码分支:
```rust ```rust
if condition == true { if condition == true {
// A... // A...
@ -31,8 +31,9 @@ fn main() {
``` ```
以上代码有以下几点要注意: 以上代码有以下几点要注意:
- **`if`语句块是表达式**: 因此它可以返回一个值,这里我们使用`if`的返回值来给`number`进行赋值,因此`number`的值是`5`。 - **`if`语句块是表达式**, 这里我们使用`if`表达式的返回值来给`number`进行赋值: `number`的值是`5`。
- 用`if`来赋值时,要保证每个分支返回的类型一样,此处返回的`5`和`6`就是同一个类型,如果返回类型不一致就会报错: - 用`if`来赋值时,要保证每个分支返回的类型一样(事实上,这种说法不完全准确,见[这里](../appendix/expressions.md#if表达式)),此处返回的`5`和`6`就是同一个类型,如果返回类型不一致就会报错
```console ```console
error[E0308]: if and else have incompatible types error[E0308]: if and else have incompatible types
--> src/main.rs:4:18 --> src/main.rs:4:18
@ -70,7 +71,7 @@ fn main() {
有一点要注意,就算有多个分支能匹配,也只有第一个匹配的分支会被执行! 有一点要注意,就算有多个分支能匹配,也只有第一个匹配的分支会被执行!
如果代码中有大量的`else if `会让代码变得极其丑陋,因此在下一章,我们引入一个`match`关键字,用以解决多分支模式匹配的问题。 如果代码中有大量的`else if `会让代码变得极其丑陋,不过不用担心,下一章的`match`专门用以解决多分支模式匹配的问题。
## 循环控制 ## 循环控制
@ -80,7 +81,7 @@ fn main() {
#### for循环 #### for循环
`for`循环是Rust处理迭代的大杀器,当我们迭代数据集合时,往往就用`for` `for`循环是Rust的大杀器:
```rust ```rust
fn main() { fn main() {
for i in 1..=5 { for i in 1..=5 {
@ -88,13 +89,15 @@ fn main() {
} }
} }
``` ```
以上代码迭代输出一个从1到5的序列简单粗暴核心就在于`for`和`in`的联动,语义表达如下:
以上代码循环输出一个从1到5的序列简单粗暴核心就在于`for`和`in`的联动,语义表达如下:
```rust ```rust
for 元素 in 集合 { for 元素 in 集合 {
// 使用元素干一些你懂我不懂的事情 // 使用元素干一些你懂我不懂的事情
} }
``` ```
这个语法跟`javascript`还蛮像,应该挺好理解。 这个语法跟`javascript`还蛮像,应该挺好理解。
注意,使用`for`我们往往使用集合的引用形式,除非你不想在后面的代码中继续使用该集合(不使用引用的话,所有权会被转移到`for`语句块中): 注意,使用`for`我们往往使用集合的引用形式,除非你不想在后面的代码中继续使用该集合(不使用引用的话,所有权会被转移到`for`语句块中):
```rust ```rust
@ -139,7 +142,7 @@ for _ in 0..10 {
**两种循环方式优劣对比** **两种循环方式优劣对比**
以下代码,我们实现了两种循环方式: 以下代码,使用了两种循环方式:
```rust ```rust
// 第一种 // 第一种
let collection = [1, 2, 3, 4, 5]; let collection = [1, 2, 3, 4, 5];
@ -154,8 +157,8 @@ for item in collection {
} }
``` ```
第一种方式是循环索引,然后通过索引下标去访问集合,第二种方式是直接循环迭代集合中的元素,优劣如下: 第一种方式是循环索引,然后通过索引下标去访问集合,第二种方式是直接循环集合中的元素,优劣如下:
- **性能**:第一种使用方式中`collection[index]`的索引访问,会因为边界检查(bounds checking)导致运行时的性能损耗 - Rust会检查并确认`index`是落在集合内也就是合法的,但是第二种直接迭代的方式就不会触发这种检查,因为编译器会在编译时就完成分析并证明这种访问是合法的` - **性能**:第一种使用方式中`collection[index]`的索引访问,会因为边界检查(bounds checking)导致运行时的性能损耗 - Rust会检查并确认`index`是落在集合内,但是第二种直接迭代的方式就不会触发这种检查,因为编译器会在编译时就完成分析并证明这种访问是合法的
- **安全**: 第一种方式里对`collection`的索引访问是非连续的,存在一定可能性在两次访问之间,`collection`发生了变化,导致脏数据产生。而第二种直接迭代的方式是连续访问,因此不存在这种风险 - **安全**: 第一种方式里对`collection`的索引访问是非连续的,存在一定可能性在两次访问之间,`collection`发生了变化,导致脏数据产生。而第二种直接迭代的方式是连续访问,因此不存在这种风险
由于for循环无需任何条件限制也不需要通过索引来访问因此是最安全也是最常用的在下面的`while`中,我们能看到为什么`for`会更加安全。 由于for循环无需任何条件限制也不需要通过索引来访问因此是最安全也是最常用的在下面的`while`中,我们能看到为什么`for`会更加安全。
@ -193,7 +196,7 @@ fn main() {
} }
``` ```
该`while`循环,只有`n`小于等于`5`时,才执行,否则就立刻跳出循环,因此在上述代码中,它会先从`0`开始,满足条件,进行循环,然后是`1`,满足条件,进行循环,最终到`6`的时候,不满足条件,跳出`while`循环,执行`我出来了`的打印,然后程序结束: 该`while`循环,只有`n`小于等于`5`时,才执行,否则就立刻跳出循环,因此在上述代码中,它会先从`0`开始,满足条件,进行循环,然后是`1`,满足条件,进行循环,最终到`6`的时候,不满足条件,跳出`while`循环,执行`我出来了`的打印,然后程序结束:
```console ```console
0! 0!
1! 1!
@ -303,7 +306,7 @@ fn main() {
println!("The result is {}", result); println!("The result is {}", result);
} }
``` ```
以上代码当`counter`递增到`10`时,就会通过`break`返回一个`counter*2`的值最后赋给result并打印出来。 以上代码当`counter`递增到`10`时,就会通过`break`返回一个`counter*2`的值,最后赋给`result`并打印出来。
这里有几点值得注意: 这里有几点值得注意:
- **break可以单独使用也可以带一个返回值**,有些类似`return` - **break可以单独使用也可以带一个返回值**,有些类似`return`

@ -27,11 +27,26 @@ fn main() {
- **X | Y**, 是逻辑运算符`或`,代表该分支可以匹配`X`也可以匹配`Y`,只要满足一个即可 - **X | Y**, 是逻辑运算符`或`,代表该分支可以匹配`X`也可以匹配`Y`,只要满足一个即可
其实`match`跟其他语言中的`switch`非常像,例如`_`类似`switch`中的`default`。 其实`match`跟其他语言中的`switch`非常像, 而且`_`类似`switch`中的`default`。
## `match`匹配 ## `match`匹配
首先来看看`match`的通用形式:
```rust
match target {
模式1 => 表达式1,
模式2 => {
语句1;
语句2;
表达式2;
},
_ => 表达式3
}
该形式清晰的说明了何为模式,何为模式匹配:将模式与`target`进行匹配,即为模式匹配,而模式匹配不仅仅局限于`match`,后面我们会详细阐述。
`match`允许我们将一个值与一系列的模式相比较,并根据相匹配的模式执行对应的代码,下面让我们来一一详解,先看一个例子: `match`允许我们将一个值与一系列的模式相比较,并根据相匹配的模式执行对应的代码,下面让我们来一一详解,先看一个例子:
```rust ```rust
enum Coin { enum Coin {
Penny, Penny,
@ -53,11 +68,11 @@ fn value_in_cents(coin: Coin) -> u8 {
} }
``` ```
`value_in_cents`函数根据匹配到的硬币类似,返回对应的美分数值,`match`后紧跟着的是一个表达式,跟`if`很像,但是`if`后的表达式必须是一个布尔值,而`match`后的表达式返回值可以是任意类型,只要能跟后面的分支匹配起来即可,这里的`coin`是枚举`Coin`类型。 `value_in_cents`函数根据匹配到的硬币类似,返回对应的美分数值,`match`后紧跟着的是一个表达式,跟`if`很像,但是`if`后的表达式必须是一个布尔值,而`match`后的表达式返回值可以是任意类型,只要能跟后面的分支中的模式匹配起来即可,这里的`coin`是枚举`Coin`类型。
接下来是`match`的分支。一个分支有两个部分:一个模式和针对该模式的处理代码。第一个分支的模式是`Coin::Penny`而之后的`=>`运算符将模式和将要运行的代码分开。这里的代码就仅仅是表达式`1`, 不同分支之间使用逗号分隔。 接下来是`match`的分支。一个分支有两个部分:**一个模式和针对该模式的处理代码**。第一个分支的模式是`Coin::Penny`而之后的`=>`运算符将模式和将要运行的代码分开。这里的代码就仅仅是表达式`1`, 不同分支之间使用逗号分隔。
当`match`表达式执行时,它将目标值`coin`按顺序与每一个分支的模式相比较, 如果模式匹配了这个值,那么模式之后的代码将被执行。如果模式并不匹配这个值,将继续执行下一个分支。 当`match`表达式执行时,它将目标值`coin`按顺序依次与每一个分支的模式相比较, 如果模式匹配了这个值,那么模式之后的代码将被执行。如果模式并不匹配这个值,将继续执行下一个分支。
每个分支相关联的代码是一个表达式,而表达式的结果值将作为整个 match 表达式的返回值。如果分支有多行代码,那么需要用`{}`包裹,同时最后一行代码需要是一个表达式。 每个分支相关联的代码是一个表达式,而表达式的结果值将作为整个 match 表达式的返回值。如果分支有多行代码,那么需要用`{}`包裹,同时最后一行代码需要是一个表达式。
@ -84,7 +99,7 @@ fn main() {
#### 模式绑定 #### 模式绑定
匹配分支的另外一个重要功能是从模式中取出绑定的值,例如: 模式匹配的另外一个重要功能是从模式中取出绑定的值,例如:
```rust ```rust
#[derive(Debug)] #[derive(Debug)]
enum UsState { enum UsState {
@ -100,7 +115,7 @@ enum Coin {
Quarter(UsState), // 25美分硬币 Quarter(UsState), // 25美分硬币
} }
``` ```
其中Coin::Quarter成员还存放了一个值美国的某个州因为在1999年到2008年间美国在25美分(Quater)硬币的背后为50个州印刷了不同的设计其它硬币都没有相关的设计。 其中Coin::Quarter成员还存放了一个值美国的某个州因为在1999年到2008年间美国在25美分(Quater)硬币的背后为50个州印刷了不同的设计, 其它硬币都没有相关的设计。
接下来我们希望在模式匹配中获取到25美分硬币上刻印的州的名称 接下来我们希望在模式匹配中获取到25美分硬币上刻印的州的名称
```rust ```rust
@ -116,7 +131,7 @@ fn value_in_cents(coin: Coin) -> u8 {
} }
} }
``` ```
上面代码中,在匹配`Coin::Quarter`模式时,我们把它内部存储的值绑定到了`state`变量上,因此`state`变量就是对应的`UsState`枚举类型。 上面代码中,在匹配`Coin::Quarter(state)`模式时,我们把它内部存储的值绑定到了`state`变量上,因此`state`变量就是对应的`UsState`枚举类型。
例如有一个印了阿拉斯加州标记的25分硬币`Coin::Quarter(UsState::Alaska))`, 它在匹配时,`state`变量将被绑定`UsState::Alaska`的枚举值。 例如有一个印了阿拉斯加州标记的25分硬币`Coin::Quarter(UsState::Alaska))`, 它在匹配时,`state`变量将被绑定`UsState::Alaska`的枚举值。
@ -185,7 +200,7 @@ fn main() {
``` ```
我们没有处理`Direction::West`的情况,因此会报错: 我们没有处理`Direction::West`的情况,因此会报错:
```rust ```console
error[E0004]: non-exhaustive patterns: `West` not covered // 非穷尽匹配,`West`没有被覆盖 error[E0004]: non-exhaustive patterns: `West` not covered // 非穷尽匹配,`West`没有被覆盖
--> src/main.rs:10:11 --> src/main.rs:10:11
| |
@ -211,7 +226,7 @@ error[E0004]: non-exhaustive patterns: `West` not covered // 非穷尽匹配,`
#### `_` 通配符 #### `_` 通配符
Rust 也提供了一个**模式**用于不想列出所有可能值的场景。例如,`u8` 可以拥有 0 到 255 的有效的值,如果我们只关心 1、3、5 和 7 这几个值,就并不想列出其它的 0、2、4、6、8、9 一直到 255 的值。所幸, 我们不必这么做, 因为可以使用使用特殊的模式 `_` 替代: Rust 也提供了一个**模式**用于不想列出所有可能值的场景。例如,`u8` 可以拥有 0 到 255 的有效的值,如果我们只关心 `1、3、5 和 7` 这几个值,就并不想列出其它的 `0、2、4、6、8、9 一直到 255` 的值。所幸, 我们不必一个一个列出所有值, 因为可以使用使用特殊的模式 `_` 替代:
```rust ```rust
let some_u8_value = 0u8; let some_u8_value = 0u8;
@ -226,10 +241,10 @@ match some_u8_value {
通过将其放置于其他分支后,`_`将会匹配所有遗漏的值。`()`表示啥都不做的意思,所以当匹配到`_`后,什么也不会发生。 通过将其放置于其他分支后,`_`将会匹配所有遗漏的值。`()`表示啥都不做的意思,所以当匹配到`_`后,什么也不会发生。
然后,在某些场景下,我们其实只关心**某一个值是否存在**,此时`match`就显得过于啰嗦还好Rust提供了`if let`. 然后,在某些场景下,我们其实只关心**某一个值是否存在**,此时`match`就显得过于啰嗦
## `if let`匹配 ## `if let`匹配
很多时候都会遇到只有一个模式的值需要被处理,其它值直接忽略的场景,用`match`处理如下: 有时会遇到只有一个模式的值需要被处理,其它值直接忽略的场景,用`match`处理如下:
```rust ```rust
let v = Some(3u8); let v = Some(3u8);
match v{ match v{
@ -238,7 +253,7 @@ match some_u8_value {
} }
```` ````
我们想要对 `Some(3)` 模式进行匹配, 同时不想处理任何其他 `Some<u8>` 值或 `None` 值。但是为了满足`match`表达式(穷尽性)的要求,必须在处理完这唯一的成员后加上 `_ => ()`,这样也要增加很多样板代码。 我们想要对 `Some(3)` 模式进行匹配, 同时不想处理任何其他 `Some<u8>` 值或 `None` 值。但是为了满足`match`表达式(穷尽性)的要求,必须在处理完这唯一的成员后加上 `_ => ()`,这样会增加不少无用的代码。
杀鸡焉用牛刀,可以用`if let`的方式来实现: 杀鸡焉用牛刀,可以用`if let`的方式来实现:
```rust ```rust
@ -285,3 +300,5 @@ fn main() {
println!("在匹配后age是{:?}",age); println!("在匹配后age是{:?}",age);
} }
``` ```
需要注意的是,**`match`中的变量覆盖其实不是那么的容易看出**,因此要小心!

@ -1,16 +1,18 @@
# 解构Option # 解构Option
在枚举那一章,提到过`Option`枚举变量是用来解决Rust中一个变量是否有值的问题,定义如下: 在枚举那章,提到过`Option`枚举, 它用来解决Rust中变量是否有值的问题,定义如下:
```rust ```rust
enum Option<T> { enum Option<T> {
Some(T), Some(T),
None, None,
} }
``` ```
总而言之,**一个变量要么有值:`Some(T)`, 要么为空: `None`**. 简单解释就是: **一个变量要么有值:`Some(T)`, 要么为空: `None`**.
那么现在的问题就是该如何去使用这个`Option`枚举类型,根据我们上一节的经验,可以通过`match`来实现。 那么现在的问题就是该如何去使用这个`Option`枚举类型,根据我们上一节的经验,可以通过`match`来实现。
> 因为`Option`,`Some``None`都包含在`prelude`中,因此你可以直接通过名称来使用它们,而无需以`Option::Some`这种形式去使用,总之,千万不要因为形式变短了,而忘记`Some`和`None`也是`Option`底下的枚举成员!
## 匹配 `Option<T>` ## 匹配 `Option<T>`
使用`Option<T>`,是为了从 `Some` 中取出其内部的 `T` 值以及处理没有值的情况,为了演示这一点,下面一起来编写一个函数,它获取一个 `Option<i32>`,如果其中含有一个值,将其加一;如果其中没有值,则函数返回 `None` 值: 使用`Option<T>`,是为了从 `Some` 中取出其内部的 `T` 值以及处理没有值的情况,为了演示这一点,下面一起来编写一个函数,它获取一个 `Option<i32>`,如果其中含有一个值,将其加一;如果其中没有值,则函数返回 `None` 值:
@ -30,22 +32,23 @@ let none = plus_one(None);
`plus_one`接受一个`Option<i32>`类型的参数,同时返回一个`Option<i32>`类型的值(这种形式的函数在标准库内随处所见),在该函数的内部处理中,如果传入的是一个`None`,则返回一个`None`切不做任何处理;如果传入的是一个`Some(i32)`,则通过模式绑定,把其中的值绑定到变量`i`上,然后返回`i+1`的值,同时用`Some`进行包裹。 `plus_one`接受一个`Option<i32>`类型的参数,同时返回一个`Option<i32>`类型的值(这种形式的函数在标准库内随处所见),在该函数的内部处理中,如果传入的是一个`None`,则返回一个`None`切不做任何处理;如果传入的是一个`Some(i32)`,则通过模式绑定,把其中的值绑定到变量`i`上,然后返回`i+1`的值,同时用`Some`进行包裹。
进一步说明,假设`plus_one`函数接受的参数值x是`Some(5)`,那么接着我们将`x`与各个分支进行比较。
为了进一步说明,假设`plus_one`函数接受的参数值x是`Some(5)` 来看看具体的分支匹配情况:
#### 传入参数`Some(5)` #### 传入参数`Some(5)`
```rust,ignore ```rust,ignore
None => None, None => None,
``` ```
首先是`None`分支,因为值`Some(5)` 并不匹配模式 `None`,所以继续匹配下一个分支。 首先是匹配`None`分支,因为值`Some(5)` 并不匹配模式 `None`,所以继续匹配下一个分支。
```rust,ignore ```rust,ignore
Some(i) => Some(i + 1), Some(i) => Some(i + 1),
``` ```
`Some(5)``Some(i)` 匹配吗?当然匹配!它们是相同的成员。`i` 绑定了 `Some` 中包含的值,所以 `i` 的值是 `5`。接着匹配分支的代码被执行,所以我们`i` 的值加一并返回一个含有值 `6` 的新 `Some` `Some(5)``Some(i)` 匹配吗?当然匹配!它们是相同的成员。`i` 绑定了 `Some` 中包含的值,因此 `i` 的值是 `5`。接着匹配分支的代码被执行,最后`i` 的值加一并返回一个含有值 `6` 的新 `Some`
#### 传入参数None #### 传入参数None
接着考虑下`plus_one` 的第二个调用,这次传入的`x` 是`None`我们进入 `match` 并与第一个分支相比较。 接着考虑下`plus_one` 的第二个调用,这次传入的`x` 是`None` 我们进入 `match` 并与第一个分支相比较。
```rust,ignore ```rust,ignore
None => None, None => None,

Loading…
Cancel
Save