Update: unified format

pull/357/head
Allan Downey 3 years ago
parent f9fb525546
commit cd31fbfbc5

@ -33,7 +33,7 @@ fn main() {
```
以上代码有以下几点要注意:
- **`if`语句块是表达式**,这里我们使用`if`表达式的返回值来给`number`进行赋值:`number`的值是`5`。
- **`if` 语句块是表达式**,这里我们使用 `if` 表达式的返回值来给 `number` 进行赋值:`number` 的值是 `5`
- 用 `if` 来赋值时,要保证每个分支返回的类型一样(事实上,这种说法不完全准确,见[这里](../appendix/expressions.md#if表达式)),此处返回的 `5``6` 就是同一个类型,如果返回类型不一致就会报错
```console
@ -99,9 +99,9 @@ for 元素 in 集合 {
// 使用元素干一些你懂我不懂的事情
}
```
这个语法跟`javascript`还蛮像,应该挺好理解。
这个语法跟 JavaScript 还蛮像,应该挺好理解。
注意,使用`for`时我们往往使用集合的引用形式除非你不想在后面的代码中继续使用该集合比如我们这里使用了container的引用。如果不使用引用的话所有权会被转移到`for`语句块中,后面就无法再使用这个集合了)
注意,使用 `for` 时我们往往使用集合的引用形式,除非你不想在后面的代码中继续使用该集合(比如我们这里使用了 `container` 的引用)。如果不使用引用的话,所有权会被转移到 `for` 语句块中,后面就无法再使用这个集合了)
```rust
for item in &container {
// ...
@ -160,10 +160,10 @@ for item in collection {
```
第一种方式是循环索引,然后通过索引下标去访问集合,第二种方式是直接循环集合中的元素,优劣如下:
- **性能**:第一种使用方式中`collection[index]`的索引访问,会因为边界检查(bounds checking)导致运行时的性能损耗 - Rust会检查并确认`index`是否落在集合内,但是第二种直接迭代的方式就不会触发这种检查,因为编译器会在编译时就完成分析并证明这种访问是合法的
- **性能**:第一种使用方式中 `collection[index]` 的索引访问,会因为边界检查(Bounds Checking)导致运行时的性能损耗 —— Rust会检查并确认 `index` 是否落在集合内,但是第二种直接迭代的方式就不会触发这种检查,因为编译器会在编译时就完成分析并证明这种访问是合法的
- **安全**:第一种方式里对 `collection` 的索引访问是非连续的,存在一定可能性在两次访问之间,`collection` 发生了变化,导致脏数据产生。而第二种直接迭代的方式是连续访问,因此不存在这种风险(这里是因为所有权吗?是的话可能要强调一下)
由于for循环无需任何条件限制也不需要通过索引来访问因此是最安全也是最常用的通过与下面的`while`的对比,我们能看到为什么`for`会更加安全。
由于 `for` 循环无需任何条件限制,也不需要通过索引来访问,因此是最安全也是最常用的,通过与下面的 `while` 的对比,我们能看到为什么 `for` 会更加安全。
#### `continue`
使用 `continue` 可以跳过当前当次的循环,开始下次的循环:
@ -183,7 +183,7 @@ for item in collection {
#### while循环
如果你需要一个条件来循环,当该条件为`true`时,继续循环,条件为`false`,跳出循环,那么`while`就非常适用:
如果你需要一个条件来循环,当该条件为 `true` 时,继续循环,条件为 `false`,跳出循环,那么 `while` 就非常适用:
```rust
fn main() {
let mut n = 0;
@ -209,7 +209,7 @@ fn main() {
我出来了!
```
当然,你也可以用其它方式组合实现,例如`loop`(无条件循环,将在下面介绍) + `if` + `break`
当然,你也可以用其它方式组合实现,例如 `loop`(无条件循环,将在下面介绍) + `if` + `break`
```rust
fn main() {
let mut n = 0;
@ -251,9 +251,9 @@ the value is: 40
the value is: 50
```
数组中的所有五个元素都如期被打印出来。尽管 index 在某一时刻会到达值 5不过循环在其尝试从数组获取第六个值会越界之前就停止了。
数组中的所有五个元素都如期被打印出来。尽管 `index` 在某一时刻会到达值 5不过循环在其尝试从数组获取第六个值会越界之前就停止了。
但这个过程很容易出错;如果索引长度不正确会导致程序 panic。这也使程序更慢因为编译器增加了运行时代码来对每次循环的每个元素进行条件检查。
但这个过程很容易出错;如果索引长度不正确会导致程序 ***panic***。这也使程序更慢,因为编译器增加了运行时代码来对每次循环的每个元素进行条件检查。
`for`循环代码如下:
```rust
@ -270,9 +270,9 @@ fn main() {
#### loop循环
对于循环而言,`loop`循环毋庸置疑,是适用面最高的,它可以适用于所有循环场景(虽然能用,但是在很多场景下,`for`和`while`才是最优选择)因为loop就是一个简单的无限循环,你可以在内部实现逻辑通过`break`关键字来控制循环何时结束。
对于循环而言,`loop` 循环毋庸置疑,是适用面最高的,它可以适用于所有循环场景(虽然能用,但是在很多场景下, `for``while` 才是最优选择),因为 `loop` 就是一个简单的无限循环,你可以在内部实现逻辑通过 `break` 关键字来控制循环何时结束。
使用`loop`循环一定要打起精神,否则你会写出下面的跑满你一个cpu核心的疯子代码:
使用 `loop` 循环一定要打起精神,否则你会写出下面的跑满你一个 CPU 核心的疯子代码:
```rust,ignore
fn main() {
loop {

@ -93,7 +93,7 @@ match x {
}
```
Rust 知道 `c` 位于第一个模式的序列内,所以会打印出 `early ASCII letter`
Rust 知道 `'c'` 位于第一个模式的序列内,所以会打印出 `early ASCII letter`
### 解构并分解值
@ -341,7 +341,7 @@ match numbers {
#### 使用下划线开头忽略未使用的变量
如果你创建了一个变量却不在任何地方使用它Rust 通常会给你一个警告,因为这可能会是个bug。但是有时创建一个不会被使用的变量是有用的,比如你正在设计原型或刚刚开始一个项目。这时你希望告诉 Rust 不要警告未使用的变量,为此可以用下划线作为变量名的开头:
如果你创建了一个变量却不在任何地方使用它Rust 通常会给你一个警告,因为这可能会是个 BUG。但是有时创建一个不会被使用的变量是有用的,比如你正在设计原型或刚刚开始一个项目。这时你希望告诉 Rust 不要警告未使用的变量,为此可以用下划线作为变量名的开头:
```rust
fn main() {
@ -389,7 +389,7 @@ println!("{:?}", s);
#### 用 `..` 忽略剩余值
对于有多个部分的值,可以使用 `..` 语法来只使用部分值而忽略其它值,这样也不用再为每一个被忽略的值都单独列出下划线。`..` 模式会忽略模式中剩余的任何没有显式匹配的值部分.
对于有多个部分的值,可以使用 `..` 语法来只使用部分值而忽略其它值,这样也不用再为每一个被忽略的值都单独列出下划线。`..` 模式会忽略模式中剩余的任何没有显式匹配的值部分
```rust
struct Point {
@ -470,7 +470,7 @@ match num {
}
```
这个例子会打印出 `less than five: 4`。当 `num` 与模式中第一个分支匹配时,`Some(4)` 可以与 `Some(x)`匹配,接着匹配守卫检查 `x` 值是否小于 `5`,因为 `4` 小于 `5`,所以第一个分支被选择。
这个例子会打印出 `less than five: 4`。当 `num` 与模式中第一个分支匹配时,`Some(4)` 可以与 `Some(x)` 匹配,接着匹配守卫检查 `x` 值是否小于 5因为 4 小于 5所以第一个分支被选择。
相反如果 `num``Some(10)`,因为 10 不小于 5 ,所以第一个分支的匹配守卫为假。接着 Rust 会前往第二个分支,因为这里没有匹配守卫所以会匹配任何 `Some` 成员。

@ -23,7 +23,7 @@ fn main() {
}
```
这里我们想去匹配`dire`对应的枚举类型因此在match中用三个匹配分支来完全覆盖枚举变量`Direction`的所有成员类型,有以下几点值得注意:
这里我们想去匹配 `dire` 对应的枚举类型,因此在 `match` 中用三个匹配分支来完全覆盖枚举变量 `Direction` 的所有成员类型,有以下几点值得注意:
- `match` 的匹配必须要穷举出所有可能,因此这里用 `_` 来代表未列出的所有可能性
- `match` 的每一个分支都必须是一个表达式,且所有分支的表达式最终返回值的类型必须相同
- **X | Y**,是逻辑运算符 `或`,代表该分支可以匹配 `X` 也可以匹配 `Y`,只要满足一个即可
@ -71,13 +71,13 @@ 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` 按顺序依次与每一个分支的模式相比较,如果模式匹配了这个值,那么模式之后的代码将被执行。如果模式并不匹配这个值,将继续执行下一个分支。
每个分支相关联的代码是一个表达式,而表达式的结果值将作为整个 match 表达式的返回值。如果分支有多行代码,那么需要用`{}`包裹,同时最后一行代码需要是一个表达式。
每个分支相关联的代码是一个表达式,而表达式的结果值将作为整个 `match` 表达式的返回值。如果分支有多行代码,那么需要用 `{}` 包裹,同时最后一行代码需要是一个表达式。
#### 使用 `match` 表达式赋值
还有一点很重要,`match` 本身也是一个表达式,因此可以用它来赋值:
@ -98,7 +98,7 @@ fn main() {
println!("{}", ip_str);
}
```
因为这里匹配到`_`分支,所以将`"::1"`赋值给了`ip_str`.
因为这里匹配到 `_` 分支,所以将 `"::1"` 赋值给了 `ip_str`
#### 模式绑定
@ -223,7 +223,7 @@ error[E0004]: non-exhaustive patterns: `West` not covered // 非穷尽匹配,`
= note: the matched value is of type `Direction`
```
不禁想感叹,`Rust`的编译器真**强大忍不住爆粗口了sorry如果你以后进一步深入使用Rust也会像我这样感叹的。Rust编译器清晰地知道`match`中有哪些分支没有被覆盖, 这种行为能强制我们处理所有的可能性,有效避免传说中价值十亿美金的`null`陷阱。
不禁想感叹,Rust 的编译器**真强大**忍不住爆粗口了sorry如果你以后进一步深入使用 Rust 也会像我这样感叹的。Rust 编译器清晰地知道 `match` 中有哪些分支没有被覆盖, 这种行为能强制我们处理所有的可能性,有效避免传说中价值**十亿美金** `null` 陷阱。
#### `_` 通配符
@ -263,10 +263,10 @@ if let Some(3) = some_u8_value {
}
```
这两种匹配对于新手来说,可能有些难以抉择,但是只要记住一点就好:**当你只要匹配一个条件,且忽略其他条件时就用`if let`否则都用match**。
这两种匹配对于新手来说,可能有些难以抉择,但是只要记住一点就好:**当你只要匹配一个条件,且忽略其他条件时就用 `if let` ,否则都用 `match`**。
## matches!宏
Rust标准库中提供了一个非常实用的宏:`matches!`,它可以将一个表达式跟模式进行匹配,然后返回匹配的结果`true` or `false`
Rust 标准库中提供了一个非常实用的宏:`matches!`,它可以将一个表达式跟模式进行匹配,然后返回匹配的结果 `true` or `false`
例如,有一个动态数组,里面存有以下枚举:
```rust
@ -285,7 +285,7 @@ fn main() {
v.iter().filter(|x| x == MyEnum::Foo);
```
但是,实际上这行代码会报错,因为你无法将`x`直接跟一个枚举成员进行比较。好在,你可以使用`match`来完成,但是会导致代码更为啰嗦,是否有更简洁的方式?答案是使用`matches!`:
但是,实际上这行代码会报错,因为你无法将 `x` 直接跟一个枚举成员进行比较。好在,你可以使用 `match` 来完成,但是会导致代码更为啰嗦,是否有更简洁的方式?答案是使用 `matches!`
```rust
v.iter().filter(|x| matches!(x, MyEnum::Foo));
```

@ -7,7 +7,7 @@ enum Option<T> {
None,
}
```
简单解释就是: **一个变量要么有值:`Some(T)`, 要么为空: `None`**.
简单解释就是: **一个变量要么有值`Some(T)`, 要么为空:`None`**。
那么现在的问题就是该如何去使用这个 `Option` 枚举类型,根据我们上一节的经验,可以通过 `match` 来实现。

@ -124,7 +124,7 @@ fn main() {
print_coordinates(&point);
}
```
`&(3,5)`会匹配模式`&(x,y)`,因此`x`得到了`3``y`得到了`5`.
`&(3, 5)` 会匹配模式 `&(x, y)`,因此 `x` 得到了 `3``y` 得到了 `5`
#### if 和 if let

Loading…
Cancel
Save