Parcourir la source

配置了vuepress首页,上传了六个Rust文档 (#10)

* 配置了vuepress首页,上传了六个Rust文档
Cherry il y a 9 mois
Parent
commit
a603f7016c

+ 0 - 1
.github/workflows/build.yml

@@ -42,4 +42,3 @@ jobs:
       with:
         deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }}
         publish_dir: ./docs/.vuepress/dist
-

+ 19 - 5
docs/.vuepress/config.js

@@ -2,12 +2,26 @@ module.exports = {
   base: '/rust_camp_tutorial/',
   title: 'Rust训练营教程文档',
   description: 'DragonOS-Rust camp',
+  head: [           // 注入到当前页面的 HTML <head> 中的标签
+      [
+        'link', 
+        { 
+          rel: 'icon',
+          href: '/logo.png' 
+        }
+      ], 
+      // 自定义的网页标签图标
+    ],
   themeConfig: {
-    nav: [
-		{
-			text: '首页', link: '/'
-		},
-	],
+    logo: '\logo.png',
+    nav: [                // 导航栏配置
+		  {
+			  text: '首页', link: '/'
+		  },
+	  ],
+
+    sidebar: 'auto',    // 侧边栏配置
+
 
   }
 }

BIN
docs/.vuepress/public/logo.png


+ 17 - 1
docs/README.md

@@ -1 +1,17 @@
-# Hello VuePress
+---
+home: true
+heroImage: \logo.png
+heroText: Rust训练营教程文档
+tagline: HELLO Welcome!
+actionText: 开始学习 →
+# actionLink: /Rust文档/Rust入门教程(一)/
+actionLink: /guide/
+features:
+- title: 简洁至上
+  details: 以 Markdown 为中心的项目结构,以最少的配置帮助你专注于写作。
+- title: Vue驱动
+  details: 享受 Vue + webpack 的开发体验,在 Markdown 中使用 Vue 组件,同时可以使用 Vue 来开发自定义主题。
+- title: 高性能
+  details: VuePress 为每个页面预渲染生成静态的 HTML,同时在页面被加载的时候,将作为 SPA 运行。
+footer: Copyright © 2019-2020 Zewen(Zevin) Jia
+---

+ 815 - 0
docs/Rust文档/Rust入门教程(一).md

@@ -0,0 +1,815 @@
+# Rust入门教程(一)
+
+## Rust基本介绍
+
+>Rust 语言是一种高效、可靠的通用高级语言。其高效不仅限于开发效率,它的执行效率也是令人称赞的,是一种少有的兼顾开发效率和执行效率的语言。    ——来自菜鸟教程
+
+>高性能 - Rust 速度惊人且内存利用率极高。由于没有运行时和垃圾回收,它能够胜任对性能要求特别高的服务,可以在嵌入式设备上运行,还能轻松和其他语言集成。
+>可靠性 - Rust 丰富的类型系统和所有权模型保证了内存安全和线程安全,让您在编译期就能够消除各种各样的错误。
+>生产力 - Rust 拥有出色的文档、友好的编译器和清晰的错误提示信息,还集成了一流的工具——包管理器和构建工具,智能地自动补全和类型检验的多编辑器支持, 以及自动格式化代码等等。
+
+
+## 一、变量与可变性
+
+声明变量用 `let` 关键字,默认情况下该变量是不可变的(immutable)
+
+声明变量时在前面添加一个 `mut` 关键字,便可以使得该关键字可以被修改
+
+### 变量与常量
+
+常量(constant)在绑定值之后也是不可变的,但是它与不可变变量有很多区别:
+
+- 不可以使用 `mut` 修饰,常量永远都是不可变的
+- 声明常量用 `const` 关键字,它的类型必须被标注
+- 常量可以在任何作用域内被声明,包括全局作用域
+- 常量只可以绑定到常量表达式,无法绑定到函数的调用结果或只能在运行时才能计算出的值
+
+在程序运行期间,常量在其声明的作用域内一直有效.
+
+命名规范:Rust 中使用全大写字符,每个单词之间用下划线分隔,例如:`MAX_NUM`。一个声明的例子:`const MAX_NUM: u32 = 100_000`。(注:数字中也可以添加下划线增强数字的可读性)
+
+### Shadowing
+
+可以使用相同的名字声明新的变量,新的变量会 `Shadowing(隐藏)` 之前声明的同名变量。
+
+`shadow` 和把变量标记为 `mut` 是不一样的:
+
+- 如果不使用 `let` 关键字,那么重新给非 `mut` 变量赋值就会编译错误
+- 而使用 `let` 声明的新变量,也是不可变的
+- 使用 `let` 声明的同名变量,**类型可以改变**
+
+例:
+```rust
+let string = "STRING";
+let string = string.len();
+```
+
+这样将字符串提取出他的长度,而不用单独再开一个新的变量。
+
+## 二、数据类型
+
+`Rust` 是静态语言,在编译时必须知道所有变量的类型
+
+- 基于使用的值,编译器通常能够推断出他的具体类型
+- 但是如果可能的类型很多,(比如 `String` 转为整数的 `parse` 方法)那么就要添加类型的标注,否则会报错
+
+### 标量类型
+
+一个标量类型代表一个单独的值
+
+一共有 **整数类型,浮点类型,布尔类型,字符类型** 四种类型
+
+**整数类型:**
+
+没有小数部分,如 `u32`,`i32`,`i64`等,表格如下:
+
+|Length|Signed|Unsigned|
+|---|---|---|
+|8-bit   |  i8  |  u8  |
+|16-bit  |  i16 |  u16 |
+|32-bit  |  i32 |  u32 |
+|64-bit  |  i64 |  u64 |
+|arch    | isize| usize|
+
+其中 `isize` 和 `usize` 由计算机架构的位数所决定,主要使用场景是对某种集合进行索引操作
+
+除了 `byte` 类型外,所有数值的字面值都可以加上类型后缀,例如:`58u8`。其中 `Rust` 中整数默认值为 `i32`
+
+**整数溢出**
+
+将一个 `u8` 类型的值设置为 `256`,在调试模式下编译会发生 `panic`,但是在发布模式(--release)下,编译器不会检查可能导致 `panic` 的溢出,如果溢出,将会执行 “环绕”,即 256 为 0,257 为 1,不会导致 `panic`
+
+**浮点类型:**
+
+包含 `f32`(单精度) 和 `f64`(双精度)两种浮点类型,统一采用 `IEEE-754` 标准。
+
+浮点类型的默认类型为 `f64`.
+
+**数值操作:**
+
+和其他语言一致
+
+**布尔类型:**
+
+布尔类型有 `true` 和 `false` 两个值,占用 1 字节,符号是 `bool`
+
+**字符类型:**
+
+`Rust` 语言中使用 `char` 来表示单个字符,字符的字面值采用单引号,占用 4 字节大小,是 `Unicode` 的标量值,可以表示比 `ASCII` 码多得多的内容,例如中文,日文,emoji表情等
+
+`Unicode` 标量值的范围是从 `U+0000` 到 `U+D7FF`,`U+E0000` 到 `U+10FFFF`
+
+但 Unicode 中没有字符的概念,所以直觉上认为的字符也许与 `Rust` 中的概念并不相符
+
+### 复合类型
+
+复合类型可以将多个值放在一个类型里
+
+Rust 提供了两种基础的复合类型:元组(Tuple),数组
+
+**Tuple**
+
+Tuple 可以将多个类型的多个值放在一个类型里
+
+Tuple 长度是固定的,一旦声明就无法改变
+
+创建和调用举例:
+
+```rust
+let tup:(u32, i64, f32) = (2022, -461, 6.2);
+println!("{}, {}, {}", tup.0, tup.1, tup.2);
+```
+
+**数组**
+
+数组是在栈(Stack)上分配的单个块的内存
+
+数组也可以将多个值放在一个类型里,但是数组中每个元素类型必须一致,数组长度也是固定,一旦声明不能改变
+
+创建和调用举例:
+
+```rust
+let a = [1, 2, 3, 4, 5];
+println!("{}", arr[2]);
+```
+
+如果想将数据存放在栈中而不是堆中,或者想保留固定数量的元素,可以使用数组。
+
+当然如果希望数组长度变得灵活,可以使用 `vector`
+
+**数组的类型**
+
+用 `[类型; 长度]` 这样的形式表示
+
+例
+
+```rust
+let a: [i32; 5] = [1, 2, 3, 4, 5];
+```
+
+若数组中元素都相同,则有另一种声明数组的方法:
+
+```rust
+let a = [3; 5];
+//这就相当于
+let a = [3, 3, 3, 3, 3];
+```
+
+在中括号里先指定初始值,然后是分号,然后是元素个数。
+
+使用索引来访问数组元素,如果访问的索引超过数组范围,编译会通过,运行时会报错,**但是 Rust 中不允许继续访问越界的地址**(在 C 语言中是允许的,只不过会输出乱码)
+
+## 三、函数
+
+声明函数使用 `fn` 
+
+依照惯例,针对函数和变量名,Rust 使用 `snake case` 命名规范
+- 所有字母都是小写,单词之间用下划线隔开
+- Rust 的函数调用不按照声明顺序执行,即在后面声明的函数也可以在前面调用(这点与 C 语言不同)
+
+**函数的参数**
+
+parameter(定义函数的参数),arguments(调用函数的参数)
+
+必须声明每个参数的类型
+
+
+**函数体中的语句和表达式**
+
+- 语句 `statement` 和表达式 `expression`
+- 函数体由一系列语句组成,可选的由一个表达式结束
+- Rust 是一个基于表达式的语言
+- 语句是执行一些动作的指令
+- 表达式会计算产生一个值
+- 函数的定义也是语句
+- 语句不返回值,因此不能用 let 将一个语句赋值个一个变量
+
+```rust
+let y = {
+        let x = 4;
+        x + 2       
+    };
+
+    println!("y = {}", y);
+```
+
+这里 let y 后面定义了一个代码块,这个块就是一个表达式,`x + 2` 后面没有分号,是一个表达式,相当于这个块表达式的返回值,因此最后输出的结果为 `y = 6`.
+
+而如果 `x + 2` 后面加了分号,这就是一个语句了,语句返回一个空的元组,即返回 `()`,则输出一个空的元组将会报错
+
+**函数的返回值**
+
+在 `->` 符号后面声明函数返回值的类型,但是不可以为返回值命名
+
+在 Rust 中,返回值通常就是函数体中最后一个表达式的值(大多数函数都是默认使用最后一个表达式作为返回值)
+
+若想提前返回,需要使用 `return` 关键字,并指定一个值
+
+```rust
+fn main {
+    let x = plus_five(12);
+    println!("The function return a num {}", x);
+}
+
+fn plus_five(x: i32) -> i32 {
+    x + 5
+}
+```
+
+**条件判断**
+
+只有一点说明:`if` 条件判断中表达式**必须**是 `bool` 类型,(C 语言等语言可以将类型转成 bool 再判断,Rust 中不可以)
+
+当使用了超过一个 `else-if` 时,最好使用 `match` 语句进行重构。例:
+
+```rust
+fn condition_match() {
+    let x = 3;
+    match x % 4 {
+        4 => println!("The number {} can be divided by 4", x),
+        3 => println!("The number {} can be divided by 3", x),
+        2 => println!("The number {} can be divided by 2", x),
+        _ => println!("The number {} can't be divided by 4; 3 and 2", x)    //_ 表示 default
+    }
+}
+```
+
+**在 let 语句中使用 if**
+
+因为 `if` 是一个表达式,因此可以将其放在 `let` 语句等号的右边
+
+```rust
+let condition = true;
+let x = if condition { 5 } else { 6 };
+println!("{}", x);
+```
+
+最后返回 x 的值为 5
+
+## 四、 控制流
+
+Rust 提供三种循环:`loop`、`while` 和 `for`
+
+### loop循环
+
+loop 关键字将反复执行一块代码,直到手动停止,或者使用 `break` 停止
+
+```rust
+fn branch() {
+    let mut counter = 1;
+    let x = loop {
+        counter += 1;
+        if counter == 10 {
+            break counter * 2
+        }
+    };
+    println!("The value of counter is {}", x);
+}
+```
+
+最后输出结果为 20
+
+### while循环
+
+```rust
+fn fn_while() {
+    let mut number = 3;
+    while number != 0 {
+        println!("{}!", number);
+        number = number - 1;
+    }
+    println!("MOVE! NOW!");
+}
+```
+
+### for循环
+
+```rust
+fn fn_for() {
+    let a = [10, 11, 12, 13, 14];
+    for elem in a.iter() {
+        println!("The value is {}", elem);
+    }
+}
+```
+
+**使用 for 循环实现 while 循环**
+
+`Range` 由标准库提供,指定一个开始数字和结束数字,`Range` 可以生成他们之间的一个数字(左闭右开),`rev` 方法可以翻转 `Range`。例:
+
+```rust
+fn fn_range_for() {
+    for elem in (1..4).rev() {
+        println!("{}!", elem);
+    }
+    println!("Go!");
+}
+```
+
+## *五、所有权
+
+### 所有权和堆栈
+
+所有权是 Rust 中最独特的特性,它让 Rust 无需 GC 就可以保证内存安全。
+
+**什么是所有权**
+
+- Rust 的核心就是所有权
+- 所有程序在运行时都必须管理它们使用计算机内存的方式
+  - 有些语言有垃圾收集机制(GC),在程序运行时,它们会不断寻找不再使用的内存
+  - 在其他例如 C/C++ 语言中,程序员必须显式地分配和释放内存
+- Rust 采用了第三种方式
+  - 内存是通过一个所有权系统来管理的,其中包含一组编译器在编译时检查的规则
+  - 当程序运行时,所有权特性不会减慢程序的运行速度,因为 Rust 对内存的管理相关的工作都提前到了编译时
+
+**堆内存和栈内存**
+
+- 所有存储在 Stack 上的数据必须拥有已知的固定的大小
+- 编译时大小未知的数据或运行时大小可能发生变化的数据必须存放在 Heap 上
+- Heap 的分配和 C 语言类似,在内存空间中找到一块足够大的空间,然后返回一个指针。这叫做“分配”
+- 把值压到 Stack 上不叫“分配”
+- 将值压到 Stack 上比在 Heap 上快得多,因为操作系统不需要找用来存储数据的空间,那个位置永远在 Stack 最顶端
+
+**访问数据**
+
+- 访问 Heap 的速度比 Stack 慢,因为要通过指针才能找到
+- 在 Heap 上分配大量空间也是需要很多时间的
+
+**所有权解决的问题**
+
+- 跟踪代码的哪些部分正在使用 Heap 的哪些数据
+- 最小化 Heap 上的重复数据量
+- 清理 Heap 上未使用的空间以避免空间不足
+
+**所有权规则**
+
+- 每个值都有一个变量,这个变量是这个值的所有者
+- 每个值同时只能拥有一个所有者
+- 当所有者超出作用域(Scope)时,该值将被删除
+
+**String**
+
+- 在 Heap 上分配,能够存储在编译时未知数量的文本
+- 使用 `from` 函数从字符串字面值创建出 `String` 类型
+- `let s = String::from("Cherry");`
+- 这类字符串是可以被修改的
+
+```rust
+fn main() {
+    let mut s = String::from("Hello");
+    s += ", Rust";
+    s.push_str(", Rust");
+    println!("{}", s);
+}
+```
+
+- 字符串字面值,在编译时就知道它的内容了,其文本内容直接被硬编码到最终的可执行文件里——速度快、高效。是因为其不可变性。
+- String类型,为了支持可变性,需要在 heap 上分配内存来保存编译时未知的文本内容:操作系统必须在运行时来请求内存。这步通过调用`String:from` 来实现
+- 当用完String 之后,需要使用某种方式将内存返回给操作系统这步,在拥有 GC 的语言中,GC 会跟踪并清理不再使用的内存。没有 GC,就需要我们去识别内存何时不再使用,并调用代码将它返回。―如果忘了,那就浪费内存。
+  - 如果提前做了,变量就会非法
+  - 如果做了两次,也是 Bug。必须一次分配对应一次释放
+
+但是 Rust 采用了不同的方式:对于某个值来说,当拥有它的变量走出作用域时,内存会自动交还给操作系统
+
+`drop` 函数,当变量走出作用域时,会调用这个函数
+
+**变量与数据交互的方式:Move**
+
+- String 的组成由三部分组成:指向数据的指针、长度和容量
+
+- 这些数据放在 Stack 中
+- 字符串数据存放在 Heap 中
+- 长度 len,就是存放字符串内容所需的字节数
+- 容量 capacity 指的是 String 从系统中获得内存的总字节数
+
+![String的组成](https://raw.githubusercontent.com/CherryYang05/PicGo-image/master/images/20220511181243.png)
+
+当把 s1 赋值给 s2 时,String 的数据被复制了一份,这实际上只复制了指针、长度和容量这一数据,在堆中的数据并没有被复制。
+
+![String浅拷贝](https://raw.githubusercontent.com/CherryYang05/PicGo-image/master/images/20220511202210.png)
+
+因此当变量离开作用域的时候,Rust 会自动调用 drop 函数,并将变量使用的 heap 内存释放掉。而在 s1 和 s2 都离开作用域的时候,它们都会尝试释放相同的内存,这时就出现了严重的`二次释放(double free)bug`
+
+为了保证内存安全,Rust 中没有尝试复制堆中被分配的内存,Rust 让 s1 失效:当 s1 离开作用域的时候,Rust 不需要释放任何东西
+
+当 s2 创建之后再使用 s1 的效果由下例展示:
+
+```rust
+fn test02() {
+    let s1 = String::from("Owner of Rust#");
+    let s2 = s1; 
+    println!("{}", s1);
+}
+```
+
+当创建 s2 之后,将 s1 的值赋值给 s2 之后,编译器会报如下的错:
+
+```rust
+➜  ~/Code/rust/owner git:(master) ✗ cargo run
+   Compiling owner v0.1.0 (/home/cherry/Code/rust/owner)
+warning: unused variable: `s2`
+  --> src/main.rs:22:9
+   |
+22 |     let s2 = s1;
+   |         ^^ help: if this is intentional, prefix it with an underscore: `_s2`
+   |
+   = note: `#[warn(unused_variables)]` on by default
+
+error[E0382]: borrow of moved value: `s1`
+  --> src/main.rs:24:20
+   |
+20 |     let s1 = String::from("Owner of Rust#");
+   |         -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait
+21 | 
+22 |     let s2 = s1;
+   |              -- value moved here
+23 |     
+24 |     println!("{}", s1);
+   |                    ^^ value borrowed here after move
+   |
+   = note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+For more information about this error, try `rustc --explain E0382`.
+warning: `owner` (bin "owner") generated 1 warning
+error: could not compile `owner` due to previous error; 1 warning emitted
+```
+
+也许这跟浅拷贝(shadow copy)比较类似,但是 Rust 同时还让 s1 失效了,因此用一个新的术语 `move` 来形容。同时 Rust 也隐含了一个设计原则:即 Rust 不会自动创建数据的深拷贝,通俗的说就是一块内存只能有一个变量进行操作。就运行时性能而言,任何自动赋值操作都是廉价的。
+
+**变量与数据交互的方式:Clone**
+
+要想对 heap 上面的数据进行深拷贝,可以使用 `clone` 方法,`clone` 是 `copy` 子集。例子如下:
+
+```rust
+fn test02() {
+    let s1 = String::from("Owner of Rust#");
+    let s2 = s1.clone; 
+    println!("{}", s1);
+}
+```
+
+Stack上的数据:复制
+
+- Copy trait,可以用于像整数这样完全存放在stack上面的类型
+- 如果一个类型实现了 Copy 这个 trait,那么旧的变量在赋值后仍然可用
+- 如果一个类型或者该类型的一部分实现了 Drop trait,那么 Rust 不允许让它再去实现 Copy trait 了
+
+标准库文档里有说,std::ops::Drop 这个 trait 与 Copy_trait 无法共存于一个类型,因为在 Move 时,若发生 Copy 行为,Copy 行为是隐式的,因为是隐式的,编译器很难预测什么时候调用 Drop 函数,而实现了 Clone_trait 的,因为 clone 是显式的,需要 a.clone() 这样,那么编译器就能通过这种显式的 clone,确定被 clone 的变量的位置,决定何时调用 drop 函数。
+
+**一些拥有 Copy trait 的类型**
+
+- 任何简单标量的组合类型都可以是 Copy 的
+- 任何需要分配内存或某种资源的都不是 Copy 的
+- 一些拥有 Copy trait 的类型:–所有的整数类型
+  - 例如 u32-bool
+  - char
+  - 所有的浮点类型,例如 f64
+  - Tuple(元组),如果其所有的字段都是 Copy 的
+    - (i32, i32) 是
+    - (i32, String) 不是
+
+```rust
+fn test02() {
+    let s1 = String::from("Owner of Rust#");
+    take_ownership(s1);
+    println!("{}", s1);     //报错,因为 s1 被 take_ownership 调用过后就会释放掉
+    let x = 20;
+    makes_copy(x);
+    println!("{}", x);
+}
+
+fn take_ownership(string: String) {
+    println!("{}", string);
+}
+
+fn makes_copy(num: u32) {
+    println!("{}", num);
+}
+```
+
+**返回值与作用域**
+
+函数在返回值的过程中也会发生所有权的转移,下面的例子可以很好的帮助理解所有权这一概念:
+
+```rust
+fn test03() {
+    let s1 = give_ownership();
+
+    let s2 = String::from("Rust");
+
+    let s3 = take_and_give_ownership(s2);
+}
+
+fn give_ownership() -> String {
+    let string = String::from("$Rust$");
+    string
+}
+
+fn take_and_give_ownership(string: String) -> String {
+    string
+}
+```
+
+其中 s2 在函数 `take_and_give_ownership` 调用后,所有权转移到了函数中,随着函数执行完,s2 的所有权也没有了。实际上函数的作用就是获得 s2 的所有权,然后这个所有权又返回给了 s3.
+
+一个变量的所有权总是遵循同样的模式:
+- 把一个值赋给其它变量时就会发生移动
+- 当一个包含 heap 数据的变量离开作用域时,它的值就会被drop 函数清理,除非数据的所有权移动到另一个变量上
+
+那么如何让函数使用某个值,而不获得其所有权?例子如下:
+
+```rust
+fn test04() {
+    let s1 = String::from("Welcome!");
+
+    let (s2, len) = calc_len(s1);
+
+    println!("The string {}'s length is {}.", s2, len);
+}
+
+fn calc_len(str: String) -> (String, usize) {
+    let len = str.len();
+    (str, len)
+}
+```
+
+我们将 s1 作为参数传递进去,返回一个包含 String 和 usize 类型的元组,这样就将 s1 的所有权转移给了 s2。
+
+那么如果不要传递参数能做到吗?下一节进行介绍。
+
+### 引用与借用
+
+```rust
+fn test05() {
+    let s = String::from("引用与借用");
+    let len = calc_len_2(&s);
+    println!("The string {}'s length is {}.", s, len);
+}
+
+fn calc_len_2(str: &String) -> usize {
+    str.len()
+}
+```
+
+- 参数类型是 `&String` 而不是 `String`
+- `&`就表示引用,允许引用某些值而不得到其所有权
+
+![引用](https://raw.githubusercontent.com/CherryYang05/PicGo-image/master/images/20220512190841.png)
+
+注:Rust 中解引用的符号和 C/C++ 中是一样的,都是 `*`.
+
+- 把引用作为函数参数的行为就叫**借用**
+- 和变量一样,引用也是默认不能被修改的
+- 若要使其能够修改,需要加上 `mut` 关键字
+
+例子如下:
+
+```rust
+fn test05() {
+    let mut s = String::from("引用");
+    let len = calc_len_2(&mut s);
+    println!("The string {}'s length is {}.", s, len);
+}
+
+fn calc_len_2(str: &mut String) -> usize {
+    str.push_str("与借用");
+    str.len()
+}
+```
+
+若修改了一个引用对象,则会报这样的错误:
+
+`cannot borrow *str as mutable, as it is behind a & reference`
+
+**可变引用**
+
+可变引用有一个重要的限制:在特定作用域内,对某一块数据,只能有一个可变的引用。
+- 这样做的好处是可在编译时防止数据竞争
+
+以下三种行为下会发生数据竞争:
+- 两个或多个指针同时访问同一个数据一至少有一个指针用于写入数据
+- 没有使用任何机制来同步对数据的访问
+- 可以通过创建新的作用域,来允许非同时的创建多个可变引用
+
+例:
+
+```rust
+fn test06() {
+    let mut s = String::from("Hello");
+    let s1 = &mut s;
+    let s2 = &mut s;
+    println!("{}, {}", s1, s2);
+}
+```
+
+这里 s1 和 s2 同时对可变变量 s 进行了引用,就会报这样的错误:`cannot borrow s as mutable more than once at a time`
+
+通过创建新的作用域,可以允许非同时的创建多个可变引用
+
+```rust
+fn test06() {
+    let mut s = String::from("Hello");
+    {
+        let s1 = &mut s;
+    }
+    let s2 = &mut s;
+}
+```
+
+**另一个限制**
+
+- 不可以同时拥有一个可变引用和一个不变的引用(因为不可变引用的作用就是不让值改变,被可变引用改变后,不可变引用就失去了其作用)
+- 多个不变的引用是可以的
+
+```rust
+fn test06() {
+    let mut s = String::from("Hello");
+    let s2 = &s;
+    let s3 = &s;
+    let s4 = &mut s;    //报错
+    println!("{} {} {}", s2, s3, s4);
+}
+```
+
+这样便会报错:`cannot borrow s as mutable because it is also borrowed as immutable`
+
+**悬空引用 Dangling References**
+
+悬空指针(Dangling Pointer): 一个指针引用了内存中的某个地址,而这块内存可能已经释放并分配给其它人使用了。
+- 在Rust里,编译器可保证引用永远都不是悬空引用
+- 如果你引用了某些数据,编译器将保证在引用离开作用域之前数据不会离开作用域
+
+```rust
+fn test07() {
+    let r = dangle();
+}
+
+fn dangle() -> &String {
+    let s = String::from("Dangling reference");
+    &s
+}
+```
+程序在 `dangle` 函数中声明了一个字符串,期望返回其的引用,但是函数结束后 s 便离开了他的作用域,即被销毁,因此返回的引用为空。这和 C 语言中返回局部变量的地址如出一辙,但是 Rust 在编译时就将避免这样的问题发生。
+
+报错:`missing lifetime specifier`
+
+**引用的规则**
+
+在任何给定的时刻,只能满足下列条件之一:一个可变的引用,或者任意数量不可变的引用,而且引用必须一直有效。
+
+## 六、切片
+
+Rust 的另一种不持有所有权的数据类型:切片(Slice)
+
+下面编写这样一个函数进行示范:
+
+- 它接收字符串作为参数
+- 返回它在这个字符串里找到的第一个单词
+- 如果函数没有找到任何空格,则返回整个字符串
+
+```rust
+fn main() {
+    let mut s = String::from("Hello World");
+    let space_index = first_word(&s);
+    s.clear();
+    println!("The first blank's position is in {}.", space_index);
+}
+
+fn first_word(s: &String) -> usize {
+    let bytes = s.as_bytes();
+    /*
+        byte 的 iter 方法会为数组 byte 创建一个迭代器,这个方法会依次返回集合中的每个元素
+        enumerate 方法会将 iter 返回的结果进行包装,并把每个结果作为一个元组的一部分进行返回
+        元组的第一个元素就是遍历的索引,第二个元素就是数组中的元素(是一个引用),这里实际用到的是模式匹配
+        声明了两个变量对这个元组进行解构
+    */
+    for (i, &item) in bytes.iter().enumerate() {
+        if item == b' ' {
+            return i;
+        }
+    }
+    s.len()
+}
+```
+
+实际上这个函数的设计有一个缺陷,这个函数是将字符串空格的索引位置返回,而一旦这个结果脱离了这个字符串的上下文,这个返回值便没有了意义。换句话说,这个索引位置的结果是独立于字符串而存在的,在函数返回以后,我们就再也无法保证其有效性。举个例子,若函数获取 `Hello World` 这个字符串的空格位置,获取到函数返回值为 `5` 后,将该字符串清空 `s.clear()`,但是此时函数返回值 `space_index` 的值仍然是 `5`,这跟现在的字符串便没有了任何关联,因此这个返回值便没有了意义了。这样的 API 需要关注两者之间的同步性,但是往往都会比较繁琐。
+
+Rust 提供了切片类型用来解决这一问题。
+
+**字符串切片**
+
+字符串切片是指向字符串中一部分内容的引用
+
+形式:[开始索引..结束索引],前闭后开
+
+![字符串切片](https://raw.githubusercontent.com/CherryYang05/PicGo-image/master/images/20220513022342.png)
+
+切片是放在 stack 上,右边的数组是放在 heap 上的。
+
+*【更正】:s 切片的长度和容量应该为 11.*
+
+```rust
+fn main() {
+    let mut s = String::from("Hello World");
+    let hello = &s[0..5];
+    let world = &s[6..11];
+}
+```
+
+这里切片有三个语法糖,若切片的开始位置为 0,则可以省略写,若切片的末尾时字符串最后一个位置,即等于字符串长度,那么也可以省略不写,下面的例子和上面是等价的:
+
+```rust
+fn main() {
+    let mut s = String::from("Hello World");
+
+    let hello = &s[..5];
+    let world = &s[6..];
+    let whole = &s[..];
+
+    println!("{}, {}", hello, world);   //输出为 Hello, World
+    println!("{}", whole);              //输出为 Hello World
+}
+```
+
+**注意:**
+
+- 字符串切片的范围索引必须发生在有效的 `UTF-8` 字符边界内。
+
+- 如果尝试从一个多字节的字符中创建字符串切片,程序会报错并退出
+
+下面用切片重写上面的函数:
+
+```rust
+fn main() {
+    let mut s = String::from("Hello World");
+    let space_index_slice = first_word_slice(&s);
+    s.clear();                                      //报错
+    println!("The first world is {}.", space_index_slice);
+}
+
+fn first_word_slice(s: &String) -> &str {
+    let bytes = s.as_bytes();
+    for (i, &item) in bytes.iter().enumerate() {
+        if item == b' ' {
+            return &s[..i];
+        }
+    }
+    &s[..]
+}
+```
+
+这里函数返回 `&str` 表示字符串切片,若找到空格,将返回该位置之前的字符串切片,否则返回整个字符串切片。
+
+但是上述代码中 `s.clear()` 会报错,报错信息为:
+
+```rust
+error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
+  --> src/main.rs:7:5
+   |
+5  |     let space_index_slice = first_word_slice(&s);
+   |                                              -- immutable borrow occurs here
+6  | 
+7  |     s.clear();
+   |     ^^^^^^^^^ mutable borrow occurs here
+...
+13 |     println!("The first blank's position is in {}.", space_index_slice);
+   |                                                      ----------------- immutable borrow later used here
+
+For more information about this error, try `rustc --explain E0502`.
+error: could not compile `slice` due to previous error
+```
+
+即不能将变量 s 借用为可变,因为它已经被借用为不可变。在函数参数中用了不可变引用,但是下面 `s.clear()` 又要修改字符串的值,使其变成可变,这样便会报错。
+
+**字符串字面值是切片**
+
+- 字符串字面值被直接存储在二进制程序中
+- `let s = "Hello, World!";`
+- 变量 s 的类型是 `&str`,它是一个指向二进制程序特定位置的切片
+- `&str` 是不可变引用,所以字符串字面值也是不可变的
+
+**将字符串切片作为参数传递**
+
+有经验的 Rust 开发者会采用 `&str` 作为参数类型,因为这样就可以同时接收 `String` 和 `&str` 类型的参数了
+
+- 使用字符串切片,直接调用该函数
+- 使用 `String`,可以创建一个完整的 `String` 切片来调用该函数
+- 定义函数时使用字符串切片来代替字符串引用会使我们的 API 更加通用,且不会损失任何功能
+
+```rust
+fn first_word(s: &str) -> &str {
+    //TODO
+}
+```
+
+**其他类型的切片**
+
+```rust
+fn main() {
+    let a = [1, 2, 3, 4, 5];
+    let slice = &a[1..3];
+    println!("{}", slice[1]);
+}
+```
+
+这个切片类型为 `&[i32]`,它存储了一个指向起始元素的位置的指针,还存储了一个长度,该例中为 `2`

+ 298 - 0
docs/Rust文档/Rust入门教程(三).md

@@ -0,0 +1,298 @@
+# Rust 入门教程(三)
+
+**Rust 的代码组织**
+
+代码组织主要包括:
+
+- 哪些细节可以暴露,哪些细节是私有的一作用域内哪些名称有效
+- 模块系统:
+  - Package(包):Cargo 的特性,让你构建、测试、共享 crate
+  - Crate(单元包): 一个模块树,它可产生一个 library 或可执行文件
+  - Module(模块)、use:让你控制代码的组织、作用域、私有路径
+  - Path(路径):为 struct、function 或 module 等项命名的方式
+
+
+## 1. Package 和 Crate
+
+Crate的类型:
+- binary
+- library
+
+Crate Root:
+- 是源代码文件
+- Rust编译器从这里开始,组成你的 Crate 的根Module
+
+一个 Package:
+  - 包含 1 个 Cargo.toml,它描述了如何构建这些 Crates 
+  - 只能包含 0-1 个 library crate
+  - 可以包含任意数量的 binary crate
+  - 但必须至少包含一个 crate (library 或 binary)
+
+**Cargo 的惯例**
+
+`src/main.rs`:
+- binary crate 的 crate root
+- crate 名与 package 名相同
+
+`src/lib.rs`:
+- package 包含一个 library crate
+- library crate 的 crate root
+- crate 名与 package 名相同
+
+Cargo 把 crate root 文件交给 rustc 来构建 library 或 binary
+
+如果 package 下有一个lib.rs,就说明 package 下面有一个 library crate,这个 lib.rs 就是这个 library crate 的入口文件,crate 名字也是叫 my-project.
+
+一个 Package 可以同时包含 src/main.r s和 src/lib.rs
+- 一个 binary crate,一个 library crate
+- 名称与 package 名相同
+
+一个 Package 可以有多个 binary crate:
+- 文件放在 src/bin
+- 每个文件是单独的 binary crate
+
+**Crate 的作用**
+
+将相关功能组合到一个作用域内,便于在项目间进行共享,防止冲突。例如 rand crate,访问它的功能需要通过它的名字:rand
+
+**定义 module 来控制作用域和私有性**
+
+Module:
+- 在一个crate内,将代码进行分组
+- 增加可读性,易于复用
+- 控制项目(item)的私有性。public、private
+
+建立 module:
+- mod 关键字
+- 可嵌套
+- 可包含其它项(struct、enum、常量、trait、函数等)的定义
+
+```rust
+mod front_of_house {
+    mod hosting {
+        fn add_to_waitlist() {}
+        fn seat_at_table() {}
+    }
+    
+    mod serving {
+        fn take_order() {}
+        fn serve_order() {}
+        fn take_payment() {}
+    }
+}
+```
+
+![crate树形结构](https://raw.githubusercontent.com/CherryYang05/PicGo-image/master/images/20220514204827.png)
+
+`src/main.rs` 和 `src/lib.rs` 叫做 `crate roots`:
+- 这两个文件(任意一个)的内容形成了名为 crate 的模块,位于整个模块树的根部
+- 整个模块树在隐式的crate模块下
+
+
+## 2. 路径(Path)
+
+为了在 Rust 的模块中找到某个条目,需要使用路径。路径的两种形式:
+- 绝对路径:从 crate root开始,使用 crate 名或字面值 crate
+- 相对路径:从当前模块开始,使用 self,super 或当前模块的标识符路径至少由一个标识符组成,标识符之间使用`::`
+
+**私有边界(privacy boundary)**
+
+- 模块不仅可以组织代码,还可以定义私有边界
+- 如果想把函数或结构体设为私有,可以将它放到某个模块中
+- Rust中所有的条目(函数,方法,struct,enum,模块,常量)默认是私有的
+- 父级模块无法访问子模块中的私有条目
+- 子模块里可以使用所有祖先模块中的条目
+
+**Pub 关键字**
+
+使用 `pub` 关键字将某些条目标记为公共的,同为根下的模块尽管都是私有,也可以访问
+
+**super 关键字**
+
+用来访问父级模块路径中的内容,类似文件系统中的 `..`,例子如下:
+
+```rust
+mod back_of_house {
+    fn fix_incorrect_order() {
+        cook_order();
+        super::serve_order();
+        crate::serve_order();
+    }
+
+    fn cook_order() {}
+}
+fn serve_order() {}
+```
+
+**pub struct**
+
+pub 放在 struct 前:
+- struct 是公共的
+- struct 的字段默认是私有的
+- struct 的字段需要**单独设置** pub 来变成公有
+
+```rust
+mod back_of_house {
+    pub struct Breakfast {
+        pub toast: String,
+        fruit: String,
+    }
+
+    impl Breakfast {
+        pub fn summer(toast: &str) -> Breakfast {
+            Breakfast {
+                toast: String::from(toast),
+                fruit: String::from("strawberry"),
+            }
+        }
+    }
+}
+
+pub fn eat_at_restaurant() {
+    let mut meal = back_of_house::Breakfast::summer("Rye");
+    println!("I'd like {} toast please.", meal.toast);
+}
+```
+
+**pub enum**
+
+pub 放在 enum 前:
+- enum 是公共的
+- **enum 的变体也都是公共的**
+
+```rust
+mod back_of_house {
+    pub enum Appetizer {
+        Soup,
+        Salad
+    }
+}
+```
+
+## 3. use 关键字
+
+可以使用 `use` 将路径导入到作用域内,同样遵循私有性原则,即只有公共的才能被使用
+
+```rust
+mod front_of_house {
+    pub mod hosting {
+        pub fn add_to_waitlist() {}
+        fn some_function() {}
+    }
+}
+
+use crate::front_of_house::hosting;
+
+pub fn eat_at_restaurant() {
+    hosting::add_to_waitlist();
+    hosting::add_to_waitlist();
+    hosting::add_to_waitlist();
+    hosting::some_function();   //报错,因为some_function为私有
+}
+```
+
+使用 `use` 指定相对路径:`use front_of_house::hosting;`
+
+可以直接将模块中的函数导入到作用域内,但是这样便无法区分该函数是在模块中定义还是在该文件内定义,因此通常的做法都是只导入到该函数的父模块,通过父模块调用函数。
+
+对于函数和同名条目是这样,但是对于结构体和枚举,通常是指定完整路径,这是 `use` 的习惯用法。
+
+```rust
+use std::fmt;
+use std::io;
+
+fn f1() -> fmt::Result {}
+
+fn f2() -> io::Result {}
+```
+
+**as 关键字**
+
+`as` 关键字可以为引入的路径指定一个本地的别名,例子如下:
+
+```rust
+use std::fmt::Result;
+use std::io::IOResult;
+
+fn f1() -> Result {}
+fn f2() -> IOResult {}
+```
+
+**使用 pub use 重新导出名称**
+
+使用 use 将路径(名称)导入到作用域内后,该名称在此作用域内是私有的
+
+pub use:重导出
+- 将条目引入作用域
+- 该条目可以被外部代码引入到它们的作用域
+
+`pub use crate::front_of_house::hosting;`
+
+**使用外部包**
+
+1. Cargo.toml 添加依赖的包(package)
+   - https://crates.io/
+2. use 将特定条目引入作用域
+
+标准库(std)也被当做外部包,但是不需要修改 Cargo.toml 来包含 std,需要使用 use 将 std 中的特定条目引入当前作用域
+
+```rust
+use std::collections::HashMap;
+```
+
+**使用嵌套路径清理大量 use 语句**
+
+如果使用同一个包或模块下的多个条目,可以使用嵌套路径在同一行内将上述路径进行引入
+
+格式如下:`路径相同的部分::{路径不同的部分}`,例子如下:
+
+```rust
+use std::cmp::Ordering;
+use std::io;
+
+//使用嵌套路径
+use std::{cmp::Ordering, io};
+
+//若是这样的引用
+use std::io;
+use std::io::Write;
+
+//简写为
+use std::io::{self, Write};
+```
+
+**通配符\***
+
+使用 `*` 可以把路径中所有的公共条目都引入到作用域
+
+`use std::collections::*;`
+
+应用场景:
+- 测试。将所有被测试代码引入到 tests 模块
+- 有时被用于预导入(prelude)模块
+
+## 4. 将模块拆分为不同文件
+
+**将模块内容移动到其它文件**
+
+模块定义时,如果模块名后边是 `;`,而不是代码块,Rust 会从与模块同名的文件中加载内容,同时项目文件夹结构要与模块层级结构一致。随着模块的变大,该技术可以把模块中的内容移动到其他文件中。
+
+例如:文件 `main.rs`:
+
+```rust
+pub mod back_of_house;
+```
+
+文件 `back_of_house.rs`:
+
+```rust
+pub mod hosting;
+```
+
+这时必须在 `src` 下创建一个 `back_of_house` 的文件夹,里面创建一个 `hosting.rs` 文件,即为 `src/back_of_house/hosting.rs`
+
+文件 `hosting.rs`:
+
+```rust
+pub fn test() {}
+```

+ 625 - 0
docs/Rust文档/Rust入门教程(二).md

@@ -0,0 +1,625 @@
+# Rust 入门教程(二):结构体和枚举
+
+## 一、结构体的使用
+
+### 1. 定义和实例化 struct
+
+例子:
+
+```rust
+struct User {
+    username: String,
+    email: String,
+    sign_in_count: usize,
+    active: bool,
+}
+```
+
+需要注意的是,每个字段后面用逗号隔开,最后一个字段后面可以没有逗号。
+
+实例化例子:
+
+```rust
+let user1 = User {
+        email: String::from("cherry@gmail.com"),
+        username: String::from("cherry"),
+        active: true,
+        sign_in_count: 244,
+    };
+```
+
+先创建 struct 实例,然后为每个字段指定值,无需按照声明的顺序指定。
+
+但是注意不能少指定字段。
+
+用点标记法取得结构体中的字段值,一旦 struct 的实例是可变的,那么实例中的所有字段都是可变的,不会同时既存在可变的字段又存在不可变的字段。
+
+**结构体作为函数的返回值**
+
+```rust
+fn struct_build() -> User {
+    User {
+        email: String::from("cherry@gmail.com"),
+        username: String::from("cherry"),
+        active: true,
+        sign_in_count: 244,
+    }
+}
+```
+
+**字段初始化简写**
+
+当字段名与字段值对应变量相同的时候,就可以使用字段初始化简写的方式:
+
+```rust
+fn struct_build(email: String, username: String) -> User {
+    User {
+        email,
+        username,
+        active: true,
+        sign_in_count: 244,
+    }
+}
+```
+
+**struct 更新语法**
+
+当想基于某个 struct 实例创建一个新的实例时(新的实例中某些字段可能和原先相同,某些不同),若不使用 struct 更新语法,则是这样写:
+
+```rust
+let user2 = User {
+        email: String::from("paul@nba.cn"),
+        username: String::from("Chris Paul"),
+        active: user1.active,
+        sign_in_count: user1.sign_in_count,
+    };
+```
+
+而使用 struct 更新语法,则是这样写:
+
+```rust
+let user3 = User {
+        email: String::from("paul@nba.cn"),
+        username: String::from("Chris Paul"),
+        ..user1
+    };
+```
+
+用 `..user1` 表示该实例中未赋值的其他字段和实例 `user1` 中的值一致。
+
+**Tuple Struct**
+
+可以定义类似 Tuple 的 Struct,叫做 Tuple Struct。Tuple struct 整体有个名,但里面的元素没有名
+
+适用:想给整个 tuple 起名,并让它不同于其它 tuple,而且又不需要给每个元素起名
+
+```rust
+struct Color(i32, i32, i32);
+struct Point(i32, i32, i32);
+let black(0, 0, 0);
+let origin = Point(0, 0, 0);
+```
+
+black 和 origin 是不同的类型,是不同 tuple struct 的实例
+
+**Unit-Like Struct(没有任何字段)**
+
+可以定义没有任何字段的 struct,叫做 `Unit-Like struct`,因为与 `()` 和单元类型类似,适用于需要在某个类型上实现某个trait,但是在里面又没有想要存储的数据
+
+**struct 数据所有权**
+
+再来看这个例子:
+
+```rust
+struct User {
+    username: String,
+    email: String,
+    sign_in_count: usize,
+    active: bool,
+}
+```
+
+这里的字段使用了 `String` 而不是 `&str`,原因如下:
+- 该 struct 实例拥有其所有的数据
+- 只要 struct 实例是有效的,那么里面的字段数据也是有效的 struct 里也可以存放引用,但这需要使用生命周期(以后讲)
+- 若字段为 `&str`,当其有效作用域小于该实例的作用域,该字段被清理时,实例未清理,访问该字段属于悬垂引用(类似野指针)
+- 生命周期保证只要 struct 实例是有效的,那么里面的引用也是有效的
+- 如果 struct 里面存储引用,而不使用生命周期,就会报错
+
+### 2. struct 例子
+
+一个简单的例子:计算长方形的面积
+
+```rust
+fn main() {
+    let width = 25;
+    let length = 12;
+    let area = area_of_rectangle(width, length);
+    println!("The Area of Rectangle is {}.", area);
+}
+
+fn area_of_rectangle(width: usize, length: usize) -> usize {
+    width * length
+}
+```
+
+上面这个例子很简单,但是长方形的长和宽没有联系起来,width 和 length 是两个分离的没有逻辑联系的变量,我们考虑用元组将其联系起来:
+
+```rust
+fn main() {
+    let rect = (25, 12);
+    println!("The Area of Rectangle is {}.", area_of_rectangle(rect));
+}
+
+fn area_of_rectangle(rect: (u32, u32)) -> u32 {
+    rect.0 * rect.1
+}
+```
+
+但是这样仿佛可读性变差了,我们再用结构体实现:
+
+```rust
+struct Rectangle {
+    width: u32,
+    length: u32,
+}
+
+fn main() {
+    let rect = Rectangle {
+        width: 35,
+        length: 12,
+    };
+    println!("The Area of Rectangle is {}.", area_of_rectangle(&rect));
+}
+
+fn area_of_rectangle(rect: &Rectangle) -> u32 {
+    rect.length * rect.width
+}
+```
+
+函数的参数使用结构体的借用,是为了不获得该实例的所有权,主函数在函数调用之后还可以继续使用该实例。
+
+此时我们输出实例 `rect`,会报这样的错误:`Rectangle doesn't implement std::fmt::Display`,即该结构体未实现 `Display` 这个 trait,而 Rust 中很多类型都是实现了这个 trait,才能将其在终端打印出来。因为结构体这种比标量类型更加复杂,打印的类型的可能性很多,因此需要用户自定义实现 `Display`。
+
+在报错的提示信息里,有个 note 提示我们可以使用 `{:?}` (或 `{:#?}`)来打印信息:
+
+```rust
+println!("{:?}", rect);
+```
+
+然而又出现了错误,这次的报错信息是:`Rectangle doesn't implement Debug`,显然 `Debug` 也是一种格式化方法,再看提示的 note:`add #[derive(Debug)] to Rectangle or manually impl Debug for Rectangle`,我们在结构体前添加 `#[derive(Debug)]`,使得该结构体派生与 `Debug` 这个 trait。
+
+最终完整的程序如下:
+
+```rust
+#[derive(Debug)]
+struct Rectangle {
+    width: u32,
+    length: u32,
+}
+
+fn main() {
+    let rect = Rectangle {
+        width: 35,
+        length: 12,
+    };
+
+    println!("The Area of Rectangle is {}.", area_of_rectangle(&rect));
+    println!("{:?}", rect);
+}
+
+fn area_of_rectangle(rect: &Rectangle) -> u32 {
+    rect.length * rect.width
+}
+```
+
+输出结果为:
+
+```rust
+➜  ~/Code/rust/area_of_rectangle git:(master) ✗ cargo run
+   Compiling area_of_rectangle v0.1.0 (/home/cherry/Code/rust/area_of_rectangle)
+    Finished dev [unoptimized + debuginfo] target(s) in 0.25s
+     Running `target/debug/area_of_rectangle`
+The Area of Rectangle is 420.
+Rectangle { width: 35, length: 12 }
+```
+
+若在输出格式中间加入一个 `#`,结构体输出将更加清晰:`println!("{:?}", rect);`,输出结果为:
+
+```rust
+Rectangle {
+    width: 35,
+    length: 12,
+}
+```
+
+### 3. struct 方法
+
+方法和函数类似: fn关键字、名称、参数、返回值
+
+方法与函数不同之处:
+- 方法是在 struct(或 enum、trait 对象)的上下文中定义
+- 第一个参数是 self,表示方法被调用的 struct 实例
+
+上一节我们定义了计算长方形面积的函数,但是该函数只能计算长方形的函数,无法计算其他形状的面积,因此我们希望将函数与长方形这一结构体关联起来,例子如下:
+
+```rust
+struct Rectangle {
+    width: u32,
+    length: u32,
+}
+
+impl Rectangle {
+    fn area_of_rectangle(&self) -> u32 {
+        self.length * self.width
+    }
+}
+
+fn main() {
+    let rect = Rectangle {
+        width: 35,
+        length: 12,
+    };
+
+    println!("The Area of Rectangle is {}.", rect.area_of_rectangle());
+}
+```
+
+在impl块里定义方法,方法的第一个参数可以是 `&self`,也可以**获得其所有权**或**可变借用**,和其他参数一样。这样写可以有更良好的代码组织。
+
+**方法调用的运算符**
+
+- C/C++ 中 `object->something()` 和 `(*object).something()` 一样,但是 Rust 没有 `→` 运算符
+- Rust 会自动引用或解引用一在调用方法时就会发生这种行为
+- 在调用方法时,Rust 根据情况自动添加 `&`、`&mut` 或 `*`,以便 object 可以匹配方法的签名
+- 下面这两种写法效果相同
+  - `p1.distance(&p2);`
+  - `(&p1).distance(&p2);`
+
+**方法参数**
+
+```rust
+impl Rectangle {
+    fn area_of_rectangle(&self) -> u32 {
+        self.length * self.width
+    }
+
+    fn can_hold(&self, other: &Rectangle) -> bool {
+        self.length > other.length && self.width > other.width 
+    }
+}
+```
+
+**关联函数**
+
+可以在 impl 块里定义不把 self 作为第一个参数的函数,它们叫关联函数(不叫方法)
+
+例如:`String::from()`
+
+关联函数通常用于构造器,例子如下:
+
+```rust
+struct Rectangle {
+    width: u32,
+    length: u32,
+}
+
+
+impl Rectangle {
+    fn square(size: u32) -> Rectangle {
+        Rectangle {
+            width: size,
+            length: size,
+        }
+    }
+}
+
+fn main() {
+    let s = Rectangle::square(20);
+}
+```
+
+`::` 符号
+- 关联函数
+- 模块创建的命名空间
+
+## 二、枚举与模式匹配
+
+### 1. 枚举的定义
+
+枚举允许我们列举所有可能的类型来定义一个类型
+
+例如 IP 地址,目前只有 IPv4 和 IPv6 两种类型,我们可以定义这样的枚举类型并使用:
+
+```rust
+enum IpAddrKind {
+    V4,
+    V6,
+}
+
+fn main() {
+    let four = IpAddrKind::V4;
+    let six = IpAddrKind::V6;
+    route(four);
+    route(six);
+    route(IpAddrKind::V6);
+}
+
+fn route(ip_kind: IpAddrKind) {}
+```
+
+枚举的变体都位于标识符的命名空间下,使用两个冒号 `::` 进行分隔
+
+枚举类型是一种自定义的类型,因此它可以作为结构体里字段的类型,例子如下:
+
+```rust
+enum IpAddrKind {
+    V4,
+    V6,
+}
+
+struct IpAddr {
+    kind: IpAddrKind,
+    address: String,
+}
+
+fn main() {
+    let home = IpAddr {
+        kind: IpAddrKind::V4,
+        address: String::from("192.168.3.1"),
+    };
+}
+```
+
+**将数据附加到枚举的变体中**
+
+上述的枚举类型我们可以改为:
+
+```rust
+enum IpAddr {
+    V4(String),
+    V6(String),
+}
+```
+
+优点是:不需要使用 struct,**每个变体可以拥有不同的类型以及相关联的数据量**,例如
+
+```rust
+enum IpAddr {
+    V4(u8, u8, u8, u8),
+    V6(String),
+}
+
+fn main() {
+    let home = IpAddrKind::V4(192, 168, 1, 1);
+    let loopback = IpAddrKind::V6(String::from("::1"));
+}
+```
+
+**标准库中的 IpAddr**
+
+```rust
+struct lpv4Addr {
+    // --snip--
+}
+struct lpv6Addr {
+    // --snip--
+}
+enum lpAddr {
+    V4(lpv4Addr),
+    V6(lpv6Addr),
+}
+```
+
+**为枚举定义方法**
+
+也使用 `impl` 这个关键字
+
+```rust
+enum Message {
+    Quit,
+    Move {x: u32, y: u32},
+    Write(String),
+    ChangeColor(i32, i32, i32),
+}
+
+impl Message {
+    fn call(&self) {}
+}
+
+fn main() {
+    let q = Message::Quit;
+    let m = Message::Move{x: 10, y: 12};
+    let w = Message::Write(String::from("Hello"));
+    let c = Message::ChangeColor(0, 255, 255);
+}
+```
+
+### 2. Option 枚举
+
+- 定义于标准库中
+- 在 Prelude(预导入模块)中
+- 描述了某个值可能存在(某种类型)或不存在的情况
+
+**Rust 中没有 NULL**
+
+其它语言中:
+- Null是一个值,它表示“没有值”
+- 一个变量可以处于两种状态:空值(null)、非空 
+- Null 引用:Billion Dollar Mistake
+
+Null 的问题在于:当你尝试像使用非Null值那样使用Null值的时候,就会引起某种错误,但是 Null 的概念还是有用的:**因某种原因而变为无效或缺失的值**
+
+Rust 中类似与 NULL 的概念的枚举:`Option<T>`
+
+标准库中的定义:
+
+```rust
+enum Option<T> {
+    Some(T),
+    None,
+}
+```
+
+它包含在预导入模块(Prelude)中,可以直接使用 `Option<T>`, `Some(T)`, `None`。例子如下:
+
+```rust
+fn main() {
+    let some_num = Some(3);
+    let some_string = Some("The String");
+    let absent_num: Option<i32> = None;
+}
+```
+
+其中 `Some(3)` 编译器可以推断出来 `T` 类型为 `usize`,而 `None` 的话编译器无法推断出来,因此需要显式指定 `Option<i32>`
+
+这种设计比 NULL 好在哪?
+
+因为 `Option<T>` 和 `T` 是不同的类型,不能将 `Option<T>` 当成 `T` 使用,例子如下:
+
+```rust
+fn test02() {
+    let x: i8 = 5;
+    let y: Option<i8> = Some(5);
+    let sum = x + y;
+}
+```
+
+这样会报错,提示 `cannot add Option<i8> to i8`,表示两者不是同一个类型,若想使用 `Option<T>` 中的 `T`,则必须将其手动转换为 `T`,这种设计方式可以避免代码中 NULL 值泛滥的情况。
+
+### 3. match
+
+**强大的控制流运算符 match**
+
+允许一个值与一系列模式进行匹配,并执行匹配的模式对应的代码。模式可以是字面值、变量名、通配符...
+
+```rust
+enum Coin {
+    Penny,
+    Nickel,
+    Dime,
+    Quarter,
+}
+
+fn value_in_cents(coin: Coin) -> u8 {
+    match coin {
+        Coin::Penny => {
+            println!("Penny!");
+            1
+        },
+        Coin::Nickel => 5,
+        Coin::Dime => 10,
+        Coin::Quarter => 25,
+    }
+}
+```
+
+**绑定值的模式**
+
+匹配的分支可以绑定到被匹配对象的部分值,因此可以从 enum 变体中提取值,例子如下:
+
+```rust
+#[derive(Debug)]
+enum USState {
+    California,
+    Texas,
+}
+
+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
+        },
+
+        /* Coin::Quarter(state) 也可以这样展开写 */
+        Coin::Quarter(USState::Texas) => {
+            println!("State quarter from {:?}", USState::Texas);
+            25
+        },
+        Coin::Quarter(USState::California) => {
+            println!("State quarter from {:?}", USState::California);
+            25
+        }
+    }
+}
+
+fn test03() {
+    let c = Coin::Quarter(USState::California);
+    println!("{}", value_in_cents(c));
+}
+```
+
+**匹配 Option<T>**
+
+```rust
+fn test04() {
+    let five = Some(5);
+    let six = plus_one(five);
+    let none = plus_one(None);
+}
+
+fn plus_one(x: Option<i32>) -> Option<i32> {
+    match x {
+        None => None,
+        Some(i) => Some(i + 1)
+    }
+}
+```
+
+注意:match 匹配必须穷举所有的可能,可以使用 `_` 通配符替代其他没有列出的值
+
+```rust
+fn test05() {
+    let x = 0u8;
+    match x {
+        1 => println!("one"),
+        3 => println!("three"),
+        5 => println!("five"),
+        _ => ()
+    }
+}
+```
+
+### 4. if let
+
+`if let` 是一种比 `match` 简单的控制流,他处理只关心一种匹配而忽略其他匹配的情况,它有更少的代码,更少的缩进,更少的模板代码,但是放弃了穷举的可能,可以把 `if let` 看作是 `match` 的语法糖。
+
+`if let` 的格式如下:
+
+```rust
+if let pattern = value {
+    //TODO
+}
+```
+
+他也可以搭配 `else` 例子如下:
+
+```rust
+fn test06() {
+    let v = 8u8;
+
+    match v {
+        3 => println!("Three!"),
+        _ => ()
+    }
+
+    if let 3 = v {
+        println!("Three")
+    } else if let 5 = v {
+        println!("Five!")
+    } else {
+        println!("Others!")
+    }
+}
+```

+ 372 - 0
docs/Rust文档/Rust入门教程(五).md

@@ -0,0 +1,372 @@
+# Rust入门教程(五):错误处理
+
+## 一、panic!
+
+### 1.1 Rust 错误处理概述
+
+- Rust 的可靠性:错误处理
+  - 大部分情况下:在编译时提示错误,并处理
+- 错误的分类
+  - 可恢复
+    - 例如文件未找到,可再次尝试
+  - 不可恢复
+    - bug,例如访问的索引超出范围
+- Rust 没有类似异常的机制
+  - 可恢复错误:`Result<T, E>`
+  - 不可恢复:panic! 宏
+
+### 1.2 不可恢复的错误与 panic!
+
+- 当 panic! 宏执行
+  - 你的程序会打印一个错误信息
+  - 展开(unwind)、清理调用栈(Stack)
+  - 退出程序
+
+**为应对 panic,展开或中止(abort)调用栈**
+
+- 默认情况下,当 panic 发生
+  - 程序展开调用栈(工作量大)
+    - Rust 沿着调用栈往回走
+    - 清理每个遇到的函数中的数据
+  - 或立即中止调用栈
+    - 不进行清理,直接停止程序
+    - 内存需要 OS 进行清理
+- 想让二进制文件更小,把设置从“展开”改为“中止”
+  - 在 Cargo.toml 中适当的 profile 部分设置:
+  - `panic = 'abort'`
+
+**使用 panic! 产生的回溯信息**
+
+- panic!可能出现在
+  - 我们写的代码中
+  - 我们所依赖的代码中
+- 可通过调用 panic! 的函数的回溯信息来定位引起问题的代码
+- 通过设置环境变量 `RUST_BACKTRACE` 可得到回溯信息
+  - Windows 下:`set RUST_BACKTRACE=1 && cargo run`
+  - Unix 系下:`RUST_BACKTRACE=1 cargo run`
+- 为了获取带有调试信息的回溯,必须启用调试符号(不带 `--release`)
+
+## 二、Result 和可恢复的错误
+
+### 2.1 Result 枚举
+
+Result 枚举类型的定义:
+
+```rust
+enum Result<T, E> {
+    Ok(T),
+    Err(E),
+}
+```
+
+T:操作成功情况下 Ok 变体里返回的数据的类型
+E:操作失败情况下 Err 变体里返回的错误的类型
+
+处理 Result 的一种方式:match 表达式。和 Option 枚举一样,Result 及其变体也是由 prelude 带入作用域,例子如下:
+
+```rust
+fn test02() {
+    let file = File::open("foo.txt");
+    let f = match file {
+        Ok(file) => file,
+        Err(error) => {
+            panic!("Open File Error: {:?}", error);
+        }
+    };
+}
+```
+
+我们鼠标悬停在 file 变量上,可以看到它的类型是:`std::result::Result<std::fs::File, std::io::Error>`,说明 open 函数返回的是一个 Result 枚举,且其第一个参数就是该文件,第二个参数是 io 下的 Error 类型,包含了错误的具体信息。
+
+最终输出结果如下:
+
+```rust
+➜  ~/Code/rust/panic git:(master) ✗ cargo run
+   Compiling panic v0.1.0 (/home/cherry/Code/rust/panic)
+warning: unused variable: `f`
+  --> src/main.rs:17:9
+   |
+17 |     let f = match file {
+   |         ^ help: if this is intentional, prefix it with an underscore: `_f`
+
+warning: `panic` (bin "panic") generated 2 warnings
+    Finished dev [unoptimized + debuginfo] target(s) in 0.46s
+     Running `target/debug/panic`
+thread 'main' panicked at 'Open File Error: Os { code: 2, kind: NotFound, message: "No such file or directory" }', src/main.rs:20:13
+note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+```
+
+**匹配不同的错误**
+
+```rust
+fn test03() {
+    let f = match File::open("foo") {
+        Ok(file) => file,
+        Err(error) => match error.kind(){
+            ErrorKind::NotFound => match File::create("foo") {
+                Ok(file) => file,
+                Err(error) => panic!("Creating File Error: {:?}", error)
+            }
+            other_error => panic!("Open File Error: {:?}", other_error)
+        }
+    };
+}
+```
+
+在 Err 中也会有很多种类型的错误,我们尝试匹配不同的错误类型,例如 `NotFound`。
+
+这里使用了很多 match,尽管很有用,但是比较原始。我们可以使用 **闭包(closure)** ,Result<T, E> 有很多方法,他们使用闭包作为参数,使用 match 进行实现,使用这些方法会使得代码更简洁
+
+```rust
+fn test04() {
+    let f = File::open("foo.txt").unwrap_or_else(|error| {
+        if error.kind() == ErrorKind::NotFound {
+            File::create("foo").unwrap_or_else(|error| {
+                panic!("Creating File Error: {:?}", error);
+            })
+        } else {
+            panic!("Open File Error: {:?}", error);
+        }
+    });
+}
+```
+
+具体内容到后面再讲。
+
+### 2.2 unwrap 与 expect
+
+**unwrap**
+
+unwrap 是 match 表达式的一个快捷方法,如果 Result 结果是 Ok 则返回 Ok 里面的值,如果 Result 结果是 Err 则调用 panic!宏。以刚刚这段代码举例:
+
+```rust
+fn test02() {
+    let file = File::open("foo.txt");
+    let f = match file {
+        Ok(file) => file,
+        Err(error) => {
+            panic!("Open File Error: {:?}", error);
+        }
+    };
+}
+```
+
+unwrap 的作用类似于上面这段代码,当成功打开文件时,unwrap 就会返回 Ok 里面的值,否则就会调用 Err 代码块的代码,上面那段代码用 unwrap 就可以这样写:`let f = File::open("foo.txt").unwrap();`
+
+但是发生恐慌的信息不可以自定义,这也是 unwrap 的一个缺点,而 Rust 提供了另一个方法:expect。
+
+**expect**
+
+和 unwrap 类似,但是可以指定错误信息:`let f = File::open("foo").expect("Open File Error!!!");`,这样得到的报错信息如下:
+
+```rust
+➜  ~/Code/rust/panic git:(master) ✗ cargo run
+   Compiling panic v0.1.0 (/home/cherry/Code/rust/panic)
+warning: unused variable: `f`
+  --> src/main.rs:26:9
+   |
+26 |     let f = File::open("foo").expect("Open File Error!!!");
+   |         ^ help: if this is intentional, prefix it with an underscore: `_f`
+
+warning: `panic` (bin "panic") generated 1 warnings
+    Finished dev [unoptimized + debuginfo] target(s) in 0.22s
+     Running `target/debug/panic`
+thread 'main' panicked at 'Open File Error!!!: Os { code: 2, kind: NotFound, message: "No such file or directory" }', src/main.rs:26:31
+note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+```
+
+### 2.3 传播错误
+
+当写的函数中包含可能会执行失败的调用的时候,除了可以在函数中处理这个错误,还可以将错误返回给函数的调用者,让他们来决定如何进一步处理这个错误,这就叫做 **传播错误**
+
+```rust
+fn read_text_from_file() -> Result<String, Error> {
+    let f = File::open("foo");
+
+    let mut f = match f {
+        Ok(file) => file,
+        Err(error) => return Err(error)
+    };
+
+    let mut s = String::new();
+    match f.read_to_string(&mut s) {
+        Ok(_) => Ok(s),
+        Err(error) => Err(error)
+    }
+}
+
+fn main() {
+    let result = read_text_from_file();
+    println("{:?}", result);
+}
+```
+
+将 Result<T, E> 设置成函数返回值,这样就将错误传递给了调用者,若文件 foo 存在的话,最终便可以输出文件中的内容。
+
+Rust 中还提供了 `?` 运算符,用其来简化传播错误的操作。
+
+如果 Result 是 Ok:Ok 中的值就是表达式的结果,然后继续执行程序;
+如果 Result 是 Err:Err 就是 **整个函数** 的返回值,就像使用了 return。例子如下:
+
+
+```rust
+fn read_text_from_file_easy() -> Result<String, Error> {
+    let mut f = File::open("foo")?;
+    let mut s = String::new();
+    f.read_to_string(&mut s)?;
+    Ok(s)
+}
+```
+
+上面这段简化后的代码的含义就是,若 `?` 前 Result 类型的值是 Ok,那么 Ok 里的值就会作为表达式的返回值进行返回,若类型是 Err,那么 Err 就当做整个函数的返回值进行返回。而 `f.read_to_string(&mut s)?;` 中,若 Result 类型是 Ok,实际上里面值为空,没有用到,因此当表达式返回 Ok 后,返回一个 Ok(s) 作为函数的返回值,若类型为 Err,则将其作为函数返回值进行返回。
+
+上面这个例子还可以继续进行优化,使用链式调用:
+
+```rust
+fn read_text_from_file_easist() -> Result<String, Error> {
+    let mut s = String::new();
+    File::open("foo")?.read_to_string(&mut s)?;
+    Ok(s)
+}
+```
+
+**值得注意的是,要使用 ? 运算符,必须保证函数返回类型为 Result<T, E>**,倘若我们尝试一下函数返回类型不是 Result,将会得到这样一条报错信息:`error[E0277]: the '?' operator can only be used in a function that returns 'Result' or 'Option' (or another type that implements 'FromResidual')`
+
+因此,? 运算符只能用于返回类型为 Result 或 Option 的函数
+
+**? 运算符与 main 函数**
+
+- main 函数返回类型是:()
+- main 函数的返回类型也可以是:Result<T,E>
+
+```rust
+use std::error::Error as error;
+
+fn main() -> Result<(), Box<dyn error>> {
+    let f = File::open("foo")?;
+    Ok(())
+}
+```
+
+`Box<dyn Error>` 是 trait 对象,可以简单理解为“任何可能的错误类型”。
+
+这样就可以在 main 函数中使用 `?` 运算符了。
+
+(开始玄学 o_o)
+
+**? 与 from 函数**
+
+- `Trait std:convert::From` 上的 from 函数
+  - 用于错误之间的转换
+- 被 ? 所应用的错误,会隐式的被 from 函数处理
+- 当 ? 调用 from 函数时
+  - 它所接收的错误类型会被转化为当前函数返回类型所定义的错误类型
+- 用于:针对不同错误原因,返回同一种错误类型
+  - 只要每个错误类型实现了转换为所返回的错误类型的 from 函数
+
+### 2.4 什么时候应该使用 panic!
+
+**总体原则**
+
+在定义一个可能失败的函数时,优先考虑返回 Result,若你觉得这个错误一定无法恢复,那就可以代替调用者调用 panic!
+
+**编写示例、原型代码、测试**
+
+可以使用panic!
+- 演示某些概念: unwrap
+- 原型代码: unwrap、expect
+- 测试: unwrap、expect
+  - 测试的失败是用 panic! 进行标记的
+
+**有时你比编译器掌握更多的信息**
+
+你可以确定 Result 就是 Ok,那么可以使用 unwrap,例子如下:
+
+```rust
+use std::net::IpAddr;
+fn test06() {
+    let home: IpAddr = "192.168.3.110".parse().unwrap();
+}
+```
+
+这里我们可以确定这个 IP 地址解析出来一定是有效的,因此可以直接使用 unwrap。
+
+**错误处理的指导性建议**
+- 当代码最终可能处于损坏状态时,最好使用 panic!
+- 损坏状态(Bad state):某些假设、保证、约定或不可变性被打破
+  - 例如非法的值、矛盾的值或空缺的值被传入代码
+  - 以及下列中的一条:
+    - 这种损坏状态并不是预期能够偶尔发生的事情
+    - 在此之后,您的代码如果处于这种损坏状态就无法运行
+    - 在您使用的类型中没有一个好的方法来将这些信息(处于损坏状态)进行编码
+
+**场景建议**
+- 调用你的代码,传入无意义的参数值:panic!
+- 调用外部不可控代码,返回非法状态,你无法修复:panic!
+- 如果失败是可预期的:Result
+- 当你的代码对值进行操作,首先应该验证这些值:panic!
+
+**为验证创建自定义类型**
+
+创建新的类型,把验证逻辑放在构造实例的函数里。
+
+以第一节的猜数游戏为例:
+
+```rust
+fn main() {
+    loop {
+        //...
+        let guess = "32";
+        let guess: i32 = match guess.trim().parse() {
+            Ok(num) => num,
+            Err(_) => continue,
+        };
+
+        if guess < 1 || guess > 100 {
+            println!("The num must between 1 and 100");
+            continue;
+        }
+        //...
+    }
+}
+```
+
+这样一个功能就是判断输入的数是否符合 i32 类型,若符合的话表达式返回 num,然后再判断是否在 1~100 之间,如果不满足则继续循环。如果有多个函数中都需要类似这样的判断,则代码便会显得冗余,我们可以自定义一个验证逻辑:
+
+```rust
+pub struct Guess {
+    value: i32
+}
+
+impl Guess {
+    pub fn new(value: i32) -> Guess {
+        if value < 1 || value > 100 {
+            panic!("The guess value must between 1 and 100, got {}", value);
+        }
+
+        Guess {value}
+    }
+
+    //类似 getter 方法
+    pub fn value(&self) -> i32 {
+        self.value
+    }
+}
+
+fn guess_game() {
+    loop {
+        //...
+        let guess = "32";
+        let guess: i32 = match guess.trim().parse() {
+            Ok(num) => num,
+            Err(_) => continue,
+        };
+
+        let guess = Guess::new(guess);
+    }
+}
+```
+
+如果能够成功创建 Guess 实例的话,那么就说明值通过了验证,而不需要将验证功能写在函数里了。
+
+上述 value 方法是获得 Guess 结构体中的 value 字段值,因为结构体中的字段是私有的,外部无法直接对字段赋值。

+ 642 - 0
docs/Rust文档/Rust入门教程(六).md

@@ -0,0 +1,642 @@
+# Rust入门教程(六):泛型和特性
+
+>泛型是一个编程语言不可或缺的机制。
+>C++ 语言中用"模板"来实现泛型,而 C 语言中没有泛型的机制,这也导致 C 语言难以构建类型复杂的工程。
+>泛型机制是编程语言用于表达类型抽象的机制,一般用于功能确定、数据类型待定的类,如链表、映射表等。
+
+## 1. 泛型
+
+### 1.1 泛型介绍
+
+- 泛型可以提高代码复用能力,也就是处理重复代码的问题
+- 泛型是具体类型或其它属性的抽象代替:
+  - 你编写的代码不是最终的代码,而是一种模板,里面有一些“占位符”
+  - 编译器在编译时将**占位符**替换为具体的类型
+  - 例如:`fn largest<T>(list: &[T]) ->T {...}`
+- 类型参数
+  - 很短,通常一个字母
+  - CamelCase
+  - T: type 的缩写
+
+### 1.2 在函数定义中使用泛型
+
+泛型函数
+- 参数类型
+- 返回类型
+
+```rust
+fn main() {
+    let a = vec![10, 80, 2022, 36, 47];
+    let largest = largest(&a);
+    println!("The largest ele is {}", largest);
+}
+
+fn largest(list: &[i32]) -> i32 {
+    let mut largest = list[0];
+    for &item in list {
+        if item > largest {
+            largest = item;
+        }
+    }
+    largest
+}
+```
+
+上面这段代码是求一个集合中最大的元素,我们定义的集合是一个 `i32` 类型,但是这时如果我们要传入 `f32` 或者字符型,还用同样的逻辑判断函数的话,是会报错的,这时我们就需要用到泛型。
+
+```rust
+fn largest<T>(list: &[T]) -> T {
+    let mut largest = list[0];
+    for &item in list {
+        if item > largest {
+            largest = item;
+        }
+    }
+    largest
+}
+```
+
+我们声明了一个泛型 `T`,但是这样是会编译报错的,因为不是所有类型 T 都可以进行大小比较,只有实现了下面的 `std::cmp::PartialOrd` 的 trait 才能进行大小比较,所以要对 T 进行约束。
+
+```rust
+➜  ~/Code/rust/pattern git:(master) ✗ cargo run
+   Compiling pattern v0.1.0 (/home/cherry/Code/rust/pattern)
+error[E0369]: binary operation `>` cannot be applied to type `T`
+  --> src/main.rs:10:17
+   |
+10 |         if item > largest {
+   |            ---- ^ ------- T
+   |            |
+   |            T
+   |
+help: consider restricting type parameter `T`
+   |
+7  | fn largest<T: std::cmp::PartialOrd>(list: &[T]) -> T {
+   |             ++++++++++++++++++++++
+
+For more information about this error, try `rustc --explain E0369`.
+error: could not compile `pattern` due to previous error
+```
+
+但是把 `std::cmp::PartialOrd` 这个 trait 加上又会报其他错误,这里在后面会进行介绍。
+
+### 1.3 结构体中的泛型
+
+可以使用多个泛型的类型参数,但是也不要有太多的类型,否则代码可读性将会下降。例如:
+
+```rust
+struct Point<T, U> {
+    x: T,
+    y: U,
+}
+
+fn test01() {
+    let integer = Point{x: 2022, y: 6.1};   
+}
+```
+
+### 1.4 Enum定义中的泛型
+
+可以让枚举的变体持有泛型数据类型,例如:Option<T>, Result<T, E>
+
+```rust
+enum Option<T> {
+    Some(T),
+    None
+}
+
+enum Result<T, E> {
+    Ok(T),
+    Err(E),
+}
+```
+
+### 1.5 方法定义中使用泛型
+
+```rust
+fn test01() {
+    let integer = Point{x: 2022, y: 61};
+    println!("{}", integer.x());
+}
+
+impl<T> Point<T> {
+    fn x(&self) -> &T {
+        &self.x
+    }
+}
+```
+
+注意
+- 把 T 放在 impl 关键字后,表示在类型 T 上实现方法
+  - 例如:`impl<T> Point<T>`
+- 只针对具体类型实现方法(其余类型没实现方法)
+  - 例如: `impl Point<f32>`
+- struct 中的泛型参数可以和方法的泛型参数不同
+
+```rust
+impl<T, U> Point<T, U> {
+    fn x(&self) -> &T {
+        &self.x
+    }
+}
+
+impl<T, U> Point<T, U> {
+    fn mixup<V, W>(self, other: Point<V,W>) -> Point<T, W> {
+        Point {
+            x: self.x,
+            y: other.y  
+        }
+    }
+}
+
+fn test02() {
+    let p1 = Point{x: 61, y: 85};
+    let p2 = Point{x: "Hello", y: "Rust"};
+    let p3 = p1.mixup(p2);
+    println!("p3.x = {}, p3.y = {}", p3.x, p3.y);
+}
+```
+
+上面实现的结构体方法实际上是将第一个 Point 中的 x 和第二个 Point 的 y 结合起来形成一个新的 Point。
+
+### 1.6 泛型代码的性能
+
+使用泛型的代码和使用具体类型的代码运行速度是一样的
+- 单态化(monomorphization)
+  - 在编译时将泛型替换为具体类型的过程
+
+```rust
+let ingeter = Some(5);
+let float = Some(5.0);
+
+enum Option_i32 {
+    Some(i32),
+    None
+}
+
+enum Option_f32 {
+    Some(f32),
+    None
+}
+
+fn main() {
+    let integer = Option_i32::Some(5);
+    let float = Option_f64::Some(5.0);
+}
+```
+
+## 2. trait
+
+- Trait 告诉 Rust 编译器
+  - 某种类型具有哪些并且可以与其它类型共享的功能
+- Trait:抽象的定义共享行为
+- Trait bounds(约束):泛型类型参数指定为实现了特定行为的类型
+- Trait与其它语言的接口(interface)类似,但有些区别
+
+### 2.1 定义一个 Trait
+
+Trait的定义:把方法签名放在一起,来定义实现某种目的所必需的一组行为。
+- 关键字:trait
+- 只有方法签名,没有具体实现
+- trait 可以有多个方法:每个方法签名占一行,以 `;` 结尾
+- 实现该 trait 的类型必须提供具体的方法实现
+
+```rust
+pub trait Summary {
+    fn summarize(&self) -> String;
+}
+```
+
+### 2.2 在类型上实现 trait
+
+- 在类型上实现 trait。与为类型实现方法类似
+- 不同之处:`impl Xxxx for Tweet {...}`
+- 在 impl 的块里,需要对 Trait 里的方法签名进行具体的实现
+
+文件 `lib.rs`:
+
+```rust
+pub trait Summary {
+    fn summarize(&self) -> String;
+}
+
+pub struct NewsArticle {
+    pub headline: String,
+    pub location: String,
+    pub author: String,
+    pub content: String
+}
+
+impl Summary for NewsArticle {
+    fn summarize(&self) -> String {
+        format!("{}, by {} ({})", self.headline, self.author, self.location)
+    }
+}
+
+pub struct Tweet {
+    pub username: String,
+    pub content: String,
+    pub reply: bool,
+    pub retweet: bool
+}
+
+impl Summary for Tweet {
+    fn summarize(&self) -> String {
+        format!("{}: {}", self.username, self.content)
+    }
+}
+```
+
+文件 `main.rs`:
+
+```rust
+use trait_demo::Summary;
+use trait_demo::Tweet;
+
+fn main() {
+    let tweet = Tweet {
+        username: String::from("Cherry_ICT"),
+        content: String::from("People in Shanghai are free today..."),
+        reply: false,
+        retweet: false
+    };
+
+    println!("Get 1 new tweet: {}", tweet.summarize());
+
+}
+```
+
+实现的功能很简单,不做具体解释了。
+
+
+### 2.3 实现 trait 的约束
+
+- 可以在某个类型上实现某个 trait 的前提条件是
+  - 这个类型或这个 trait 是在本地 crate 里定义的
+- 无法为外部类型来实现外部的trait
+  - 这个限制是程序属性的一部分(也就是一致性)
+  - 更具体地说是**孤儿规则**:之所以这样命名是因为父类型不存在
+  - 此规则确保其他人的代码不能破坏您的代码,反之亦然
+  - 如果没有这个规则,两个 crate 可以为同一类型实现同一个 trait,Rust 就不知道应该使用哪个实现了
+
+**默认实现**
+
+默认实现的方法可以调用 trait 中的其他方法,即使这些方法没有默认实现,但是注意,无法从方法的重写实现中调用默认实现。
+
+```rust
+pub trait Summary {
+    fn summarize(&self) -> String {
+        format!("(Read more from {} ...)", self.summarize_author())
+    }
+
+    fn summarize_author(&self) -> String;
+}
+```
+
+在 trait 中可以有方法的默认实现,在默认实现的基础上,类型可以对该 trait 进行重载。同样,在 trait 中默认实现的方法可以实现 trait 中其他方法。
+
+### 附
+
+刚刚 trait 例子的完整代码如下:
+
+`lib.rs`:
+
+```rust
+pub trait Summary {
+    fn summarize(&self) -> String {
+        format!("(Read more from {} ...)", self.summarize_author())
+    }
+
+    fn summarize_author(&self) -> String;
+}
+
+pub struct NewsArticle {
+    pub headline: String,
+    pub location: String,
+    pub author: String,
+    pub content: String
+}
+
+impl Summary for NewsArticle {
+    fn summarize(&self) -> String {
+        format!("{}, by {} ({})", self.headline, self.author, self.location)
+    }
+
+    fn summarize_author(&self) -> String {
+        format!("@{}", self.author)
+    }
+}
+
+pub struct Tweet {
+    pub username: String,
+    pub content: String,
+    pub reply: bool,
+    pub retweet: bool
+}
+
+impl Summary for Tweet {
+    fn summarize(&self) -> String {
+        format!("{}: {}", self.username, self.content)
+    }
+
+    fn summarize_author(&self) -> String {
+        format!("@{}", self.username)
+    }
+}
+```
+
+`main.rs`:
+
+```rust
+use trait_demo::Summary;
+use trait_demo::Tweet;
+use trait_demo::NewsArticle;
+
+fn main() {
+    let tweet = Tweet {
+        username: String::from("Cherry_ICT"),
+        content: String::from("People in Shanghai are free today..."),
+        reply: false,
+        retweet: false
+    };
+
+    println!("Get 1 new tweet: {}", tweet.summarize());
+
+    let news = NewsArticle {
+        headline: String::from("WWDC will be held in June 7th"),
+        location: String::from("USA"),
+        author: String::from("Tim Cook"),
+        content: String::from("The Apple will take us a lot of devices."),
+    };
+
+    println!("You receive a news: {}", news.summarize());
+}
+```
+
+最终输出结果为:
+
+```rust
+➜  ~/Code/rust/trait_demo git:(master) ✗ cargo run
+   Compiling trait_demo v0.1.0 (/home/cherry/Code/rust/trait_demo)
+    Finished dev [unoptimized + debuginfo] target(s) in 0.33s
+     Running `target/debug/trait_demo`
+Get 1 new tweet: Cherry_ICT: People in Shanghai are free today...
+You receive a news: WWDC will be held in June 7th, by Tim Cook (USA)
+```
+
+### 2.4 实现 Trait 作为参数
+
+- impl Trait 语法:适用于简单情况
+- Trait bound 语法:可用于复杂情况
+  - impl trait 语法实际上是 trait bound 语法的语法糖
+- 使用 `+` 指定多个 trait bound
+- Trait bound 使用 where 子句
+  - 在方法签名后指定 where 子句
+
+```rust
+pub trait Summary {}
+
+pub struct NewsArticle {}
+
+impl Summary for NewsArticle {}
+
+pub struct Tweet {}
+
+impl Summary for Tweet {}
+
+pub fn notify(item: impl Summary) {
+    println!("Breaking news! {}", item.summarize());
+}
+```
+
+这是采用 impl Trait 的语法,这里的 notify 方法要求传入的参数可以是 `NewsArticle` 类型或者是 `Tweet` 类型,也就是要求参数要实现 `Summary` 这个 trait,从而使用 summarize 这个方法。
+
+```rust
+pub fn notify<T: Summary>(item: T) {
+    println!("Breaking news! {}", item.summarize());
+}
+```
+
+这是采用 Trait bound 的写法,下面这个例子讲展示出这种写法的优势:
+
+```rust
+pub fn notify<T: Summary>(item1: T, item2: T) {
+    println!("Breaking news! {}", item.summarize());
+}
+```
+
+当有多个参数时,采用这种写法可以使得代码相对简洁一些。
+
+使用 `+` 指定多个 trait bound:
+
+```rust
+pub fn notify1(item: impl Summary + Display) {
+    println!("Breaking news! {}", item.summarize());
+}
+
+pub fn notify<T: Summary + Display>(item: T) {
+    println!("Breaking news! {}", item.summarize());
+}
+```
+
+然而如果一个函数中参数过多,那么整个函数声明就会变得非常长,不太直观,可读性差,这里可以使用 where 子句来指定 trait 的约束:
+
+```rust
+pub fn notify2<T: Summary + Display, U: Clone + Debug>(a: T, b: U) -> String {
+    format!("Breaking news! {}", a.summarize())
+}
+```
+
+这个例子中函数签名太长,不够直观,采用 where 子句可以使得更加直观:
+
+```rust
+pub fn notify3<T, U>(a: T, b: U) -> String
+where
+    T: Summary + Display,
+    U: Clone + Debug,
+{
+    format!("Breaking news! {}", a.summarize())
+}
+```
+
+### 2.5 实现 Trait 作为返回类型
+
+- impl trait 语法
+  - 注意:impl Trait 只能返回确定的同一种类型,返回可能不同类型的代码会报错
+
+```rust
+pub fn notify4(flag: bool) -> impl Summary {
+    if flag {
+        NewsArticle {...}
+    } else {
+        Tweet {...}
+    } 
+}
+```
+
+这样的话这个函数便没有了确定的返回类型,这样便会报错。
+
+### 2.6 使用 trait bound 实现之前泛型 的例子
+
+我们再来看一下[之前的代码](#12-在函数定义中使用泛型)。解决如下:
+
+```rust
+fn largest<T: PartialOrd>(list: &[T]) -> T {
+    let mut largest = list[0];
+    for &item in list {
+        if item > largest {
+            largest = item;
+        }
+    }
+    largest
+}
+```
+
+之前我们说过,实际上比较大小的运算符是实现了 `std::cmp::PartialOrd` 这样一个 trait,因此我们需要指定实现这个 trait 的泛型才能进行大小比较。
+
+但是这样改完后又会出现一个问题:
+
+```rust
+➜  ~/Code/rust/pattern git:(master) ✗ cargo run 
+   Compiling pattern v0.1.0 (/home/cherry/Code/rust/pattern)
+error[E0508]: cannot move out of type `[T]`, a non-copy slice
+  --> src/main.rs:10:19
+   |
+10 | let mut largest = list[0];
+   |                   ^^^^^^^
+   |                   |
+   |                   cannot move out of here
+   |                   move occurs because `list[_]` has type `T`, which does not implement the `Copy` trait
+   |                   help: consider borrowing here: `&list[0]`
+
+error[E0507]: cannot move out of a shared reference
+  --> src/main.rs:11:18
+   |
+11 |     for &item in list {
+   |         -----    ^^^^
+   |         ||
+   |         |data moved here
+   |         |move occurs because `item` has type `T`, which does not implement the `Copy` trait
+   |         help: consider removing the `&`: `item`
+
+Some errors have detailed explanations: E0507, E0508.
+For more information about an error, try `rustc --explain E0507`.
+error: could not compile `pattern` due to 2 previous errors
+```
+
+报错原因是:无法从 list 中移除 T,因为没有实现 Copy trait,建议采用借用
+
+因为上面两个 vector 中的元素分别为整型和字符型,这两种类型有确定的大小并且都是存储在栈中,因此都实现了 Copy trait,于是在 T 的 trait 约束中再加上 Copy 即可:
+
+```rust
+fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
+    let mut largest = list[0];
+    for &item in list {
+        if item > largest {
+            largest = item;
+        }
+    }
+    largest
+}
+```
+
+但是如果将 vec 中元素类型改为 String,那么又会报错,因为 String 是存储在堆中,没有实现 Copy trait,但是实现了 Clone trait
+
+```rust
+let str_list = vec![String::from("Hello"), String::from("World")];
+let largest = get_max_ele(&str_list);
+```
+
+我们将 T 加上 Clone 约束,去掉 Copy 约束:
+
+```rust
+fn get_max_ele<T: PartialOrd + Clone>(list: &[T]) -> T {
+    let mut largest = list[0];
+    for &item in list {
+        if item > largest {
+            largest = item;
+        }
+    }
+    largest
+}
+```
+
+这样又会出现错误:
+
+```rust
+error[E0508]: cannot move out of type `[T]`, a non-copy slice
+  --> src/main.rs:21:23
+   |
+21 |     let mut largest = list[0];
+   |                       ^^^^^^^
+   |                       |
+   |                       cannot move out of here
+   |                       move occurs because `list[_]` has type `T`, which does not implement the `Copy` trait
+   |                       help: consider borrowing here: `&list[0]`
+```
+
+是因为这里 list[0] 是字符串切片,是一个借用,没有所有权,因此一个借用给一个变量赋值,这个借用对应的类型必须要实现 Copy trait。因此在 list 前面加上引用,并且将 item 也设为引用,最后返回 &T。
+
+```rust
+fn get_max_ele<T: PartialOrd + Clone>(list: &[T]) -> &T {
+    let mut largest = &list[0];
+    for item in list {
+        if item > largest {
+            largest = item;
+        }
+    }
+    largest
+}
+```
+
+若要最后还是返回 T,则可以使用 clone 方法:
+
+```rust
+fn get_max_ele<T: PartialOrd + Clone>(list: &[T]) -> T {
+    let mut largest = list[0].clone();
+    for item in list {
+        if item > &largest {
+            largest = item.clone();
+        }
+    }
+    largest
+}
+```
+
+### 2.7 使用 Trait Bound 有条件的实现方法
+
+在使用泛型类型参数的 impl 块上使用 Trait bound,我们可以有条件的为实现了特定 Trait 的类型来实现方法
+
+```rust
+struct Pair<T> {
+    x: T,
+    y: T,
+}
+
+impl<T> Pair<T> {
+    fn new(x: T, y: T) -> Self {
+        Self { x, y }
+    }
+}
+
+impl<T: Display + PartialOrd> Pair<T> {
+    fn cmd_display(&self) {
+        if self.x >= self.y {
+            println!("The largest member is x = {}", self.x);
+        } else {
+            println!("The largest member is y = {}", self.y);
+        }
+    }
+}
+```
+
+- 也可以为实现了其它 Trait 的任意类型有条件的实现某个 Trait
+- 为满足 Trait Bound 的所有类型上实现 Trait 叫做覆盖实现 (blanket implementations)
+
+```rust
+impl<T: fmt::Display> Tostring for T {}
+```
+
+含义为:为实现了 Display trait 的类型实现 ToString trait,而 ToString 中实现了 to_string 方法。
+
+例如 `let s = 3.to_string();`

+ 551 - 0
docs/Rust文档/Rust入门教程(四).md

@@ -0,0 +1,551 @@
+# Rust 入门教程(四):常用的集合
+
+这一节介绍 Rust 中常用的几种集合类型:`Vector`,`String` 和 `HashMap`,这些集合类型都是存储在 Heap 中,不需要编译时确定大小,可以在运行时动态改变大小。
+
+
+## 1. Vector
+
+Vector 概念略,和 C++ STL 中的 Vector 差不多,下面来看使用:
+
+**创建 Vector**
+
+使用 `Vec::new()` 函数创建一个空 Vector,`let v: Vec<i32> = Vec::new();`(注意 V 是大写),也可以设定初始值进行创建,这时就要使用 `vec!` 宏,注意这里 v 是小写,`let v: vec![1, 2, 3];`。
+
+**更新 Vector**
+
+使用 `push` 方法向 vector 里添加元素
+
+```rust
+fn main() {
+    let mut vec = Vec::new();
+
+    vec.push(1);
+    vec.push(2);
+    vec.push(3);
+    vec.push(4);
+}
+```
+
+使用 `Vec::new()` 创建 vector 时,需要显式指明类型,但是若下面用 push 函数添加 vector 元素时,编译器可以根据上下文推断出类型,于是便可以不用显式指定类型了。
+
+**删除 Vector**
+
+与任何其它 struct 一样,当 Vector 离开作用域后,它就被清理掉了,同时它所有的元素也被清理掉了
+
+**读取 Vector 元素**
+
+- 索引
+- get 方法
+
+```rust
+fn test01() {
+    let v = vec![1, 2, 38, 4, 45];
+    let third = &v[2];
+    println!("The third element is {}.", third);
+    match v.get(2) {
+        Some(third) => println!("The third element is {}.", third),
+        None => println!("There is no element in the vec."),
+    }
+}
+```
+
+因为 Vector 的 get 方法返回一个 Option 枚举类型,因此用 Some 和 None 做模式匹配。
+
+但是如果用索引访问的下标越界的话,就会发生 panic,**但是使用 get 的话,会返回 None.**
+
+**所有权和借用规则**
+
+```rust
+fn test02() {
+    let v = vec![1, 12, 3, 4, 5];
+    let first = &v[1];
+    v.push(6);
+    println!("{}", first);
+}
+```
+
+上面这段代码在 `v.push();` 会报错,原因是 `first` 已经借用 v 为不可变引用,而 push 操作又将 v 变成可变的,因此会报错。
+
+vector 之所以也有所有权和借用规则这样的机制,是因为 vector 是存储在堆中,并且数据是连续存放,如果在后面添加一个元素,而原来的内存中后面没有多余空间,则编译器将会在内存中重新寻找一块更大的内存来存放新的 vector,所以这是还是用原来 vector 的引用,就会发生错误。
+
+实际上 C++ 中的 vector 也是这样的,但是 Rust 的语言机制从编译时就避免了,而不用等到运行时在发现错了,这也体现了 Rust 的高安全性和可靠性。下面是 C++ 中的 vector 元素首地址改变的例子,[参考博客](https://blog.csdn.net/wch0lalala/article/details/108337051)
+
+```cpp
+#include <iostream>
+#include <vector>
+
+using namespace std;
+
+int main() {
+    vector<int> arr;
+    arr.push_back(1);
+    cout << "arr's size :" << arr.size() << " capacity :" << arr.capacity() << " head address :" << arr.data() << endl;
+    arr.push_back(2);
+    cout << "arr's size :" << arr.size() << " capacity :" << arr.capacity() << " head address :" << arr.data() << endl;
+    arr.push_back(3);
+    cout << "arr's size :" << arr.size() << " capacity :" << arr.capacity() << " head address :" << arr.data() << endl;
+    arr.push_back(4);
+    cout << "arr's size :" << arr.size() << " capacity :" << arr.capacity() << " head address :" << arr.data() << endl;
+    arr.push_back(5);
+    cout << "arr's size :" << arr.size() << " capacity :" << arr.capacity() << " head address :" << arr.data() << endl;
+    arr.push_back(6);
+    cout << "arr's size :" << arr.size() << " capacity :" << arr.capacity() << " head address :" << arr.data() << endl;
+    arr.push_back(7);
+    cout << "arr's size :" << arr.size() << " capacity :" << arr.capacity() << " head address :" << arr.data() << endl;
+    return 0;
+}
+```
+
+输出结果为:
+
+```cpp
+arr's size :1 capacity :1 head address :0x10516fb0
+arr's size :2 capacity :2 head address :0x10516e30
+arr's size :3 capacity :4 head address :0x10516fb0
+arr's size :4 capacity :4 head address :0x10516fb0
+arr's size :5 capacity :8 head address :0x10516e30
+arr's size :6 capacity :8 head address :0x10516e30
+arr's size :7 capacity :8 head address :0x10516e30
+```
+
+我们发现每次 vector 扩容时元素首地址都会改变。(注意在 vector 中,&vec 和 &vec[0] 是不一样的,这是因为 vector 结构存在栈中,而 vector 的数据存在堆中,&vec 获得的只是栈中 vec 的结构的地址,始终不变,而 &vec[0] 是堆中 vector 存储的真正的元素地址,每次动态扩容后都会改变)
+
+好我们回到 Rust。
+
+**遍历 vector**
+
+```rust
+fn test03() {
+    let v = vec![1, 2, 3, 4];
+    for i in &v {
+        println("{}", i);
+    }
+}
+```
+
+要想修改其中的值,看下面的例子:
+
+```rust
+fn test03() {
+    let mut vec![1, 2, 3, 4];
+    for i in &mut v {
+        *i += 5;
+        println("{}", i);
+    }
+}
+```
+
+这里 `*i` 是解引用,我们以后会介绍。
+
+**使用 enum 来存储多种数据类型**
+
+vector 只能存储一种数据类型,但是我们有时候想存储不同类型的数据,我们可以用可附加数据的枚举来创建 vector。
+
+```rust
+enum Spreadsheetcell {
+    Int(i32),
+    Float(f64),
+    Text(String),
+}
+
+fn test03() {
+    let v = vec![
+        Spreadsheetcell::Int(3),
+        Spreadsheetcell::Text(String::from("Computer Science")),
+        Spreadsheetcell::Float(6.18),
+    ];
+}
+```
+
+使用枚举的变体就可以在 vector 中存储不同类型的数据了。
+
+## 2. String
+
+String 这个数据类型是所有编程语言中都非常重要的一个类型,Rust 开发者经常会被字符串困扰的原因是,Rust 倾向于暴露可能的错误,而且字符串数据结构复杂并且采用了 UTF-8 编码。
+
+[编码类型和编码方式](#各种类型的编码)
+
+在 Rust 中,字符串是 Byte 的集合,并且有一些方法将 byte 解析为文本。
+
+**字符串是什么**
+
+从 Rust 语言的核心层面上将,只有一个字符串类型,叫字符串切片 `str`,它通常以引用的形式出现(即 `&str`)。
+
+字符串切片是对存储在其他地方,用 UTF-8 编码的字符串的引用。**字符串字面值:存储在二进制文件中,也是字符串切片**
+
+**String 类型,来自标准库,而不是核心语言**,他可增长、可修改、可拥有,采用 UTF-8 编码,我们通常所说的字符串是指 `String` 和 `&str` 两者,而不是单指某一种。当然这里主要将的是 `String`。
+
+**其它类型的字符串**
+
+Rust 的标准库还包含了很多其它的字符串类型,例如:OsString,OsStr、CString、CStr
+- String vs Str 后缀:拥有或借用的变体
+- 可存储不同编码的文本或在内存中以不同的形式展现
+- Library crate 针对存储字符串可提供更多的选项
+
+### 2.1 字符串的创建
+
+- 很多 `Vec<T>` 都可用于 `String`
+- 利用 `String::new()` 创建字符串
+- 使用初始值来创建 String:
+  - `to_string()` **方法**,可用于实现了 Display trait 的类型,包括字符串字面值
+  - `String::from()` **函数**,从字面值创建 String 类型
+
+```rust
+fn main() {
+    let mut s1 = String::new();
+    let mut s2 = String::from("Hello");
+    let mut s3 = "Hello, Rust";
+    let mut s4 = "Hello, Rust".to_string();
+}
+```
+
+通过字符串字面值创建的 `s3` 是切片类型,其他都是 String 类型
+
+【注】:我理解的函数就是没有 self 参数的,通过 `::` 来调用,而方法则是带有 `&self` 为其第一个参数,是通过实例来调用的,使用的是点调用的方式
+
+### 2.2 字符串更新
+
+- 使用 `push_str()` **方法**,把一个字符串切片附加到 String,这个方法不会获得参数的所有权
+- 使用 `push()` **方法**,将一个单独的字符附加到 String 后
+
+```rust
+fn test02() {
+    let mut s = String::from("Hello");
+    let s1 = String::from(", Rust");
+    s.push_str(&s1);
+    s1.push('x');
+    println!("{}", s1);
+}
+```
+
+这里还是可以打印出 s1 的值,说明没有获得 s1 的所有权。
+
+- `+` 用来连接字符串,注意加号前是**字符串类型**,后面是**字符串类型的引用**
+  - 使用了类似这个签名的方法 `fn add(self, s: &str) -> String { ... }`
+  - 标准库中 `add` 方法使用了泛型
+  - 解引用强制转换(deref coercion)
+  - 只能把 `&str` 添加到 `String`
+- `format!` 用于连接多个字符串
+
+
+```rust
+fn test03() {
+    let s1 = String::from("Hello");
+    let s2 = String::from(", Rust");
+
+    let s3 = s1 + &s2;
+    println!("{}", s3);
+    println!("{}", s1);
+    println!("{}", s2);
+}
+```
+
+上述例子中,我们发现 `s1` 不能再使用了,而 `s2` 可以再被使用,因为底层方法中使用了类似这个签名的方法 `fn add(self, s: &str) -> String { ... }`,是将字符串自身和另一个字符串的切片进行相加组合。但是 `s2` 是字符串类型,而函数参数中是字符串切片类型,不匹配,为什么代码 `let s3 = s1 + &s2;` 能够编译通过?
+
+这是因为标准库中 `add` 方法使用了泛型,Rust 采用了一种叫 **解引用强制转换(deref coercion)** 的技术,强制将字符串类型转化成字符串切片类型,因此 `s2` 的所有权就会被保留,而 `add` 函数就会取得第一个参数的所有权,当函数调用完毕时,`s1` 的所有权就会消失。
+
+```rust
+fn test04() {
+    let s1 = String::from("tic");
+    let s2 = String::from("tac");
+    let s3 = String::from("toc");
+
+    // let s3 = s1 + "-" + &s2 + "-" + &s3;
+    // println!("{}", s3);
+
+    let res = format!("{}-{}-{}", s1, s2, s3);
+
+    println!("{}", res);
+}
+```
+
+上述例子中将三个字符串之间用 `-` 连接,如果代码太多显得很繁琐,采用 `format!` 宏不仅可以使得代码简单易读,更重要的是不会取得所有变量的所有权。这个宏和 `println!` 很像,只不过 `format!` 宏是将结果返回,这里可以用一个变量去接收。
+
+### 2.3 字符串的索引
+
+不可以按照索引的语法对 String 某部分进行访问,因为 String 类型没有实现 `Index<Integer>` trait,例如:
+
+```rust
+fn test() {
+    let s = String::from("Hello");
+    let s1 = s[0];  //这样是错误的
+}
+```
+
+之所以无法通过索引进行访问是因为,字符串是用 UTF-8 进行编码,每一个字符都对应一个 Unicode 编码。在英文中,一个字符是 `1B`,而对于某些语言,一个字符可能对应 `2B`,而中文一个字符对应 `3B`,因此用索引进行访问的话,无法知道应该取一个字节还是两个字节。
+
+当然 Rust 不允许通过索引进行访问的另一个原因是,索引操作理应只消耗 `O(1)` 的时间,但是为了保证不会越界,需要使用 `O(n)` 的时间进行遍历字符串长度。
+
+**String 内部表示**
+
+String 实际上是对 `Vec<u8>` 进行的包装,可以用 `len()` 方法获得字符串长度
+
+字节(Bytes),标量值(Scalar Values),字形簇(Grapheme Clusters)
+
+Rust有三种看待字符串的方式:
+- 字节
+- 标量值
+- 字形簇(最接近所谓的“字母”)
+
+### 2.4 切割 String
+
+可以使用 `[]` 和 `一个范围` 来创建一个字符串的切片
+- 要谨慎使用
+- 如果切割时越过边界就会 panic
+
+```rust
+fn test05() {
+    let hello = String::from("你好吗");
+    let s = &hello[0..3];
+    println!("{}", s);
+}
+```
+
+上面我们说了在 Rust 中一个中文字符占 `3B`,因此我们获取字符串 hello 的前 3 个字节,输出结果为 `你`,但是要注意的是,所获取的字节必须能够构成所定义的字符,比如只获取前两字节最后运行就会报错(但是编译不会报错)。
+
+### 2.5 遍历 String
+
+- 对于标量值:chars() 方法
+- 对于字节:bytes() 方法
+- 对于字形簇,过于复杂,标准库未提供
+
+```rust
+fn test05() {
+    let hello = String::from("你好吗");
+
+    for i in hello.chars() {
+        print!("{} ", i);
+    }
+
+    println!();
+
+    for i in hello.bytes() {
+        print!("{} ", i);
+    }
+}
+```
+
+最后输出结果为:
+
+```rust
+你 好 吗 
+228 189 160 229 165 189 229 144 151
+```
+
+**String不简单**
+- Rust 选择将正确处理 String 数据作为所有 Rust 程序的默认行为——程序员必须在处理 UTF-8 数据之前投入更多的精力
+- 可防止在开发后期处理涉及非 ASCII 字符的错误
+
+### 各种类型的编码
+
+- 1967 年 ASCII,包含英文字母,阿拉伯数字,西文字符和控制字符
+- 1980 年 GB2312,包含简体中文,拉丁字母和日文假名
+- 1984 年 BIG5,增加了繁体字
+- 1993 年 GB130001,包含了中日韩三国文字
+- 1995 年 GBK,不支持韩文
+- 2000 年 GB18030,兼容更多的字符
+
+在 1994 年诞生的 Unicode 实现了编码的全球化。
+
+那么这时候如何表示字符呢,英文所需要表示的二进制位和汉字所要表示的二进制位肯定是不同的,也就是说一个英文字符可能要占 1B,而汉字要占 2B,编码时都是将二进制连接在一起,那么怎么知道当前的一个字节所要表示的是英文还是中文呢?因此直接将每个字符用编号表示无法正确区分每个字符的边界。
+
+一种解决方法是 **定长编码**,即每个字符都用统一长度表示,位数不够前面补 0。但是这样又出现了一个问题,一个英文字母要占两个字节,要忍受前面全是 0 的情况,只会占用更多的内存。
+
+![定长编码](https://raw.githubusercontent.com/CherryYang05/PicGo-image/master/images/20220519005429.png)
+
+解决方法也很简单,可以采用 **变长编码**,那么如何区分字符边界呢?其实和计组中的 **拓展操作码**,**IP 地址**,**哈夫曼编码** 都很类似,如下图所示:
+
+![变长编码](https://raw.githubusercontent.com/CherryYang05/PicGo-image/master/images/20220519005820.png)
+
+这就是 **UTF-8 编码**
+
+## 3. HashMap
+
+和各种语言中的 HashMap 一样,也是通过键值对来存储数据,通过键(Key)来寻找数据,而不是索引
+
+### 3.1 创建 HashMap
+
+- 创建空 HashMap:new() 函数
+- 添加数据:insert() 方法
+
+```rust
+use std::collections::HashMap;
+
+fn main() {
+    let mut hash: HashMap<String, i32> = HashMap::new();
+    hash.insert(String::from("Cherry"), 23);
+}
+```
+
+若声明的时候没有指定类型,则必须要插入数据才不会报错,若声明时已经指定类型了,那么不用插入数据也不会报错。
+
+- HashMap 用的较少,不在 Prelude中
+- 标准库对其支持较少,没有内置的宏来创建
+- HashMap 数据存储在 heap 上
+- 同构的。一个 HashMap 中
+  - 所有的 K 必须是同一种类型
+  - 所有的 V 必须是同一种类型
+
+**另一种创建 HashMap 的方式:collect 方法**
+
+在元素类型为 Tuple 的 Vector 上使用 collect 方法,可以组建一个 HashMap
+- 要求 Tuple 有两个值:一个作为 K,一个作为 V
+- collect 方法可以把数据整合成很多种集合类型,包括 HashMap
+- 返回值需要显式指明类型
+
+```rust
+fn test02() {
+    let teams = vec![String::from("Suns"), String::from("Lakers")];
+    let rank = vec![1, 20];
+    let nba: HashMap<_, _> = teams.iter().zip(rank.iter()).collect();
+}
+```
+
+使用 `collect` 时,要显式声明返回类型,因为它可以生成各种类型。
+
+**HashMap 和所有权**
+
+对于实现了 Copy trait 的类型(例如i32),值会被复制到 HashMap 中。对于拥有所有权的值(例如String),值会被移动,所有权会转移给 HashMap。
+
+如果将值的引用插入到 HashMap,值本身不会移动一在 HashMap 有效的期间,**被引用的值必须保持有效**。
+
+
+```rust
+fn test03() {
+    let key = String::from("Suns");
+    let value = String::from("Champion");
+    let mut map = HashMap::new();
+    map.insert(key, value);
+    println!("{} {}", key, value);      //这里会报错,因为key和value所有权已经没有了
+
+    //通常应该是这样插入数据
+    map.insert(&key, &value);
+}
+```
+
+这里在堆 HashMap 赋值之后,key 和 value 的所有权就被转移给了 HashMap,通常都是传入字符串的引用。
+
+### 3.2 访问 HashMap
+
+get方法
+- 参数:K
+- 返回:`Option<&V>`
+
+```rust
+fn test04() {
+    let mut nba = HashMap::new();
+    nba.insert("Suns".to_string(), 1);
+    nba.insert("Lakers".to_string(), 20);
+    let team = String::from("Suns");
+    let rank = nba.get(&team);
+
+    match rank {
+        Some(s) => println!("{}", s),
+        None => println!("team not exist")
+    }
+}
+```
+
+HashMap 的 get 方法返回的是一个 Option 枚举,并且 get 方法的参数是 String 的引用。
+
+### 3.3 遍历 HashMap
+
+使用 for 循环:
+
+```rust
+fn test04() {
+    let mut nba = HashMap::new();
+    nba.insert("Suns".to_string(), 1);
+    nba.insert("Lakers".to_string(), 20);
+
+    for (k, v) in &nba {
+        println!("{}: {}", k, v);
+    }
+}
+```
+
+(k, v) 这里是用元组做模式匹配。
+
+### 3.4 更新 HashMap
+
+- HashMap 大小可变
+- 每个 K 同时只能对应一个 V
+- 更新 HashMap 中的数据
+- K 已经存在,对应一个 V
+  - 替换现有的 V
+  - 保留现有的 V,忽略新的 V
+  - 合并现有的 V 和新的 V
+- K 不存在
+  - 添加一对新的 K, V
+  
+**替换现有的 V**
+
+如果向 HashMap 插入一对 (K, V),然后再插入同样的 K,但是不同的 V,那么原来的 V 会被替换掉
+
+```rust
+fn test05() {
+    let mut scores = HashMap::new();
+    scores.insert(String::from("Suns"), 115);
+    scores.insert(String::from("Suns"), 132);   //覆盖
+    println!("{:?}", scores);
+}
+```
+
+**只在 K 不对应任何值的情况下,才插入 V**
+
+entry方法
+- 检查指定的 K 是否对应一个 V
+- 参数为 K
+- 返回 enum Entry:代表值是否存在
+
+Entry 的 or_insert()方法
+- 返回
+  - 如果 K 存在,返回到对应的 V 的一个可变引用
+  - 如果 K 不存在,将方法参数作为 K 的新值插进去,返回到这个值的可变引用
+
+```rust
+fn test05() {
+    let mut scores = HashMap::new();
+    scores.insert(String::from("Suns"), 115);
+
+    scores.entry("Lakers".to_string()).or_insert(90);
+
+    println!("{:?}", scores);
+}
+```
+
+这里 `scores.entry()` 返回 Entry,如果没有值的话,会返回类似于 `Entry(VacantEntry("Lakers"))` 的枚举。
+
+**基于现有值来更新**
+
+```rust
+fn test06() {
+    let text = "What a wonderful world a a a";
+
+    let mut map = HashMap::new();
+    for word in text.split_whitespace() {
+        let count = map.entry(word).or_insert(0);
+        *count += 1;
+    }
+    println!("{:#?}", map);
+}
+```
+
+最终返回结果为:
+
+```rust
+{
+    "wonderful": 1,
+    "world": 1,
+    "a": 4,
+    "What": 1,
+}
+```
+
+### 3.5 Hash 函数
+
+默认情况下,HashMap 使用加密功能强大的 Hash 函数,可以抵抗拒绝服务(DoS)攻击。
+- 不是可用的最快的 Hash 算法
+- 但具有更好安全性。
+- 可以指定不同的 hasher 来切换到另一个函数
+- hasher 是实现 BuildHasher trait 的类型

+ 13 - 0
docs/guide/README.md

@@ -0,0 +1,13 @@
+# Rust 学习文档
+
+#### [Rust学习文档(一)](/Rust文档/Rust入门教程(一)/)
+
+#### [Rust学习文档(二)](/Rust文档/Rust入门教程(二)/)
+
+#### [Rust学习文档(三)](/Rust文档/Rust入门教程(三)/)
+
+#### [Rust学习文档(四)](/Rust文档/Rust入门教程(四)/)
+
+#### [Rust学习文档(五)](/Rust文档/Rust入门教程(五)/)
+
+#### [Rust学习文档(六)](/Rust文档/Rust入门教程(六)/)

+ 1 - 1
package.json

@@ -13,4 +13,4 @@
     "docs:build": "vuepress build docs"
   }
 
-}
+}