pull/190/head
sunface 3 years ago
commit f74f476742

@ -6,13 +6,13 @@
- 镜像: https://book.rust.team (若course.rs打不开可使用此国内镜像地址)
- 最近修订: 新增章节[线程同步锁、Condvar和信号量](https://zhuanlan.zhihu.com/p/456448504)
- Rust版本 Rust edition 2021
- QQ交流群: 1009730433
- QQ交流群1009730433
### 教程简介
**`Rust语言圣经`**涵盖从**入门到精通**所需的全部Rust知识目录及内容都经过深思熟虑的设计同时语言生动幽默行文流畅自如摆脱技术书籍常有的机器味和晦涩感。
在Rust基础教学的同时我们还提供了(部分)
- **深入度**, 在基础教学的同时, 提供了深入剖析, 浅尝辄止并不能让我们站上紫禁之巅
- **深入度**在基础教学的同时, 提供了深入剖析, 浅尝辄止并不能让我们站上紫禁之巅
- **性能优化**选择Rust, 意味着就要追求性能, 因此你需要体系化的了解性能优化
- **专题**将Rust高级内容通过专题的方式一一呈现内容内聚性极强
- **难点和错误索引**,作为一本工具书,优秀的索引能力非常重要,遗忘不可怕,找不到才可怕
@ -40,7 +40,7 @@
尤其感谢这些主要贡献者,谢谢你们花费大量时间贡献了多处`fix`和高质量的内容优化,非常感动,再次感谢~~
### 开源说明
Rust语言圣经是**完全开源**的电子书, 每个章节都至少用时4-6个小时才能初步完稿牺牲了大量休闲娱乐、陪伴家人的时间还没有任何钱赚**如果大家觉得这本书作者真的用心了,希望你能帮我们点一个`star`**,感激不尽:)
Rust语言圣经是**完全开源**的电子书每个章节都至少用时4-6个小时才能初步完稿牺牲了大量休闲娱乐、陪伴家人的时间还没有任何钱赚**如果大家觉得这本书作者真的用心了,希望你能帮我们点一个`star`**,感激不尽:)
在开源版权上,我们选择了[No License](https://www.google.com.hk/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&ved=2ahUKEwigkv-KtMT0AhXFdXAKHdI4BCcQFnoECAQQAw&url=https%3A%2F%2Fchoosealicense.com%2Fno-permission%2F&usg=AOvVaw3M2Q4IbdhnpJ2K71TF7SPB)这意味着读者可以随意的fork和阅读但是**不能私下修改后再包装分发**,如果有这方面的需求,请联系我们,望理解.
在开源版权上,我们选择了[No License](https://www.google.com.hk/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&ved=2ahUKEwigkv-KtMT0AhXFdXAKHdI4BCcQFnoECAQQAw&url=https%3A%2F%2Fchoosealicense.com%2Fno-permission%2F&usg=AOvVaw3M2Q4IbdhnpJ2K71TF7SPB)这意味着读者可以随意的fork和阅读但是**不能私下修改后再包装分发**,如果有这方面的需求,请联系我们,望理解

@ -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是一个表达式**,因此可以返回一个值

@ -1,6 +1,6 @@
# 解构Option
在枚举那章,提到过`Option`枚举, 它用来解决Rust中变量是否有值的问题定义如下:
在枚举那章,提到过`Option`枚举它用来解决Rust中变量是否有值的问题定义如下
```rust
enum Option<T> {
Some(T),
@ -11,7 +11,7 @@ enum Option<T> {
那么现在的问题就是该如何去使用这个`Option`枚举类型,根据我们上一节的经验,可以通过`match`来实现。
> 因为`Option`,`Some``None`都包含在`prelude`中,因此你可以直接通过名称来使用它们,而无需以`Option::Some`这种形式去使用,总之,千万不要因为形式变短了,而忘记`Some`和`None`也是`Option`底下的枚举成员!
> 因为`Option``Some``None`都包含在`prelude`中,因此你可以直接通过名称来使用它们,而无需以`Option::Some`这种形式去使用,总之,千万不要因为调用路径变短了,就忘记`Some`和`None`也是`Option`底下的枚举成员!
## 匹配 `Option<T>`
@ -30,10 +30,10 @@ let six = plus_one(five);
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)` 来看看具体的分支匹配情况:
为了进一步说明,假设`plus_one`函数接受的参数值x是`Some(5)`来看看具体的分支匹配情况:
#### 传入参数`Some(5)`
```rust,ignore

@ -38,7 +38,7 @@ if let PATTERN = SOME_VALUE {
```
#### while let条件循环
一个与 `if let` 结构类似的是 `while let` 条件循环,它允许只要模式匹配就一直进行 `while` 循环。下面展示了一个使用`while let`的例子:
一个与 `if let` 类似的结构`while let` 条件循环,它允许只要模式匹配就一直进行 `while` 循环。下面展示了一个使用`while let`的例子
```rust
// Vec是动态数组
let mut stack = Vec::new();
@ -54,9 +54,9 @@ while let Some(top) = stack.pop() {
}
```
这个例子会打印出 `3`、`2` 接着是 `1`。`pop` 方法取出动态数组的最后一个元素并返回 `Some(value)`,如果动态数组是空的,它返回 `None`对于`while`来说,只要 `pop` 返回 `Some` 就会一直不停的循环。一旦其返回 `None``while` 循环停止。我们可以使用 `while let` 来弹出栈中的每一个元素。
这个例子会打印出 `3`、`2` 接着是 `1`。`pop` 方法取出动态数组的最后一个元素并返回 `Some(value)`,如果动态数组是空的,将返回 `None`对于`while`来说,只要 `pop` 返回 `Some` 就会一直不停的循环。一旦其返回 `None``while` 循环停止。我们可以使用 `while let` 来弹出栈中的每一个元素。
你也可以用`loop` + `if let` 或者`match`来实现,但是会更加啰嗦。
你也可以用`loop` + `if let` 或者`match`来实现这个功能,但是会更加啰嗦。
#### for循环
```rust
@ -67,18 +67,18 @@ for (index, value) in v.iter().enumerate() {
}
```
这里使用 `enumerate` 方法产生一个迭代器,该迭代器每次迭代会返回一个`(索引,值)`形式的元组,同时用`(index,value)`来匹配。
这里使用 `enumerate` 方法产生一个迭代器,该迭代器每次迭代会返回一个`(索引,值)`形式的元组,然后用`(index,value)`来匹配。
#### let语句
```rust
let PATTERN = EXPRESSION;
```
是的, 该语句我们已经用了无数次了,它也是一种模式匹配:
是的, 该语句我们已经用了无数次了,它也是一种模式匹配
```rust
let x = 5;
```
这其中,`x`也是一种模式绑定,代表将**匹配的值绑定到变量x上**.因此在Rust中,**变量名也是一种模式**,只不过它比较朴素很不起眼罢了。
这其中,`x`也是一种模式绑定,代表将**匹配的值绑定到变量x上**。因此在Rust中,**变量名也是一种模式**只不过它比较朴素很不起眼罢了。
```rust
let (x, y, z) = (1, 2, 3);
@ -86,11 +86,11 @@ let (x, y, z) = (1, 2, 3);
上面将一个元组与模式进行匹配(**模式和值的类型必需相同!**),然后把`1,2,3`分别绑定到`x,y,z`上。
因为模式匹配要求两边的类型必须相同,导致下面的代码会报错:
模式匹配要求两边的类型必须相同,否则就会导致下面的报错:
```rust
let (x, y) = (1, 2, 3);
```
因为对于元组来说,元素个数也是类型的一部分!
对于元组来说,元素个数也是类型的一部分!
#### 函数参数
函数参数也是模式:
@ -119,15 +119,15 @@ fn main() {
```rust
let Some(x) = some_option_value;
```
因为右边的值可能为`None`,这种时候就不能进行匹配,也就是上面的代码遗漏了`None`的匹配。
因为右边的值可能`Some`,而是`None`,这种时候就不能进行匹配,也就是上面的代码遗漏了`None`的匹配。
类似`let`和`for`、`match`都必须要求完全覆盖匹配,才能通过编译。
但是对于`if let`,就可以这样使用:
但是对于`if let`,就可以这样使用
```rust
if let Some(x) = some_option_value {
println!("{}", x);
}
```
因为`if let`允许匹配一种模式,而忽略其余的模式。
因为`if let`允许匹配一种模式,而忽略其余的模式。

Loading…
Cancel
Save