From 69189b361ce577271dca2169caa985f2bd0b8822 Mon Sep 17 00:00:00 2001
From: Lion Guo <lion.guo@omnitracs.com>
Date: Sat, 2 Apr 2022 07:30:32 +0000
Subject: [PATCH 01/34] Remove wrong file name, which block checkout on Win OS

---
 src/https:/github.com | 1 -
 1 file changed, 1 deletion(-)
 delete mode 100644 src/https:/github.com

diff --git a/src/https:/github.com b/src/https:/github.com
deleted file mode 100644
index 83c831f0..00000000
--- a/src/https:/github.com
+++ /dev/null
@@ -1 +0,0 @@
-# test

From 8757e1cdfa6b1f0cee1f1a9a8f24153f372b0f61 Mon Sep 17 00:00:00 2001
From: themanforfree <56149350+themanforfree@users.noreply.github.com>
Date: Sat, 2 Apr 2022 23:39:56 +0800
Subject: [PATCH 02/34] fix wrong url

---
 src/first-try/hello-world.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/first-try/hello-world.md b/src/first-try/hello-world.md
index a14910fd..9121320e 100644
--- a/src/first-try/hello-world.md
+++ b/src/first-try/hello-world.md
@@ -4,7 +4,7 @@
 
 ## 多国语言的"世界,你好"
 
-还记得大明湖畔等你的 [VSCode IDE](https://course.rs/first-try/editor.md) 和通过 `Cargo` 创建的 [世界,你好](https://course.rs/first-try/cargo.html) 工程吗?
+还记得大明湖畔等你的 [VSCode IDE](https://course.rs/first-try/editor.html) 和通过 `Cargo` 创建的 [世界,你好](https://course.rs/first-try/cargo.html) 工程吗?
 
 现在使用 VSCode 打开 [上一节](https://course.rs/first-try/cargo.html) 中创建的 `world_hello` 工程,然后进入 `main.rs` 文件。(此文件是当前 Rust 工程的入口文件,和其它语言几无区别。)
 

From bdaedb283616124c07c9eece528eb8cdc1fa4447 Mon Sep 17 00:00:00 2001
From: Allan Downey <AllanDowney@126.com>
Date: Sun, 3 Apr 2022 10:09:37 +0800
Subject: [PATCH 03/34] update(index-list): add flow-control

---
 src/index-list.md | 48 ++++++++++++++++++++++++++++++++---------------
 1 file changed, 33 insertions(+), 15 deletions(-)

diff --git a/src/index-list.md b/src/index-list.md
index 11dd59f1..f50acd14 100644
--- a/src/index-list.md
+++ b/src/index-list.md
@@ -26,6 +26,7 @@
 | [&]                     | 引用           | 常规引用是一个指针类型,指向了对象存储的内存地址 |
 | [\*]                    | 解引用         | 解出引用所指向的值                               |
 | [@]                     | 变量绑定       | 为一个字段绑定另外一个变量                       |
+| `_`                     |                | 忽略该值或者类型                                 |
 | ['a: 'b]                | 生命周期约束   | 用来说明两个生命周期的长短                       |
 | [{:?}] {:#?}            | 打印结构体信息 | 使用 `#[derive(Debug)]` 派生实现 `Debug` 特征    |
 | A                       |                | AIntroduction                                    |
@@ -63,12 +64,14 @@
 | [变量作用域] | 所有权    | 作用域是一个变量在程序中有效的范围     |
 | [表达式]     |           | 进行求值,结尾无 `;`,有返回值         |
 | [bool 布尔]  | 布尔类型  | `true` `false`,占用 1 字节            |
+| [break]      | 循环控制  | 直接跳出当前整个循环                   |
 | B            | KWB       | BIntroduction                          |
 
 [变量遮蔽]: https://course.rs/basic/variable.html#变量遮蔽shadowing
 [变量作用域]: https://course.rs/basic/ownership/ownership.html#变量作用域
 [bool 布尔]: https://course.rs/basic/base-type/char-bool.html#布尔bool
 [表达式]: https://course.rs/basic/base-type/statement-expression.html#表达式
+[break]: https://course.rs/basic/flow-control.html#break
 
 [back](#head)
 
@@ -79,6 +82,7 @@
 | [char 字符]  | 字符类型 | 使用 `''` 表示,所有的 Unicode 值                                                   |
 | [const 常量] | constant | `const MAX_POINTS: u32 = 100_000;`                                                  |
 | [Copy 拷贝]  | 浅拷贝   | 任何基本类型的组合可以 `Copy`,不需要分配内存或某种形式资源的类型是可以 `Copy` 的。 |
+| [continue]   | 循环控制 | 跳过当前当次的循环,开始下次的循环                                                  |
 | [Clone 克隆] | 深拷贝   | 需要复制堆上的数据时,可以使用 `.clone()` 方法                                      |
 | C            | KWC      | CIntroduction                                                                       |
 
@@ -86,6 +90,7 @@
 [const 常量]: https://course.rs/basic/variable.html#变量和常量之间的差异
 [copy 拷贝]: https://course.rs/basic/ownership/ownership.html#拷贝浅拷贝
 [clone 克隆]: https://course.rs/basic/ownership/ownership.html#克隆深拷贝
+[continue]: https://course.rs/basic/flow-control.html#continue
 
 [back](#head)
 
@@ -110,12 +115,14 @@
 
 ## F
 
-| 名称     | 关键字   | 简介                     |
-| -------- | -------- | ------------------------ |
-| [浮点数] | 数值类型 | `f32`<br>`f64`(默认类型) |
-| F        | KWF      | FIntroduction            |
+| 名称       | 关键字   | 简介                         |
+| ---------- | -------- | ---------------------------- |
+| [浮点数]   | 数值类型 | `f32`<br>`f64`(默认类型)     |
+| [for 循环] | 循环控制 | `for item in &collection {}` |
+| F          | KWF      | FIntroduction                |
 
 [浮点数]: https://course.rs/basic/base-type/numbers.html#浮点类型
+[for 循环]: https://course.rs/basic/flow-control.html#for-循环
 
 [back](#head)
 
@@ -140,9 +147,14 @@
 
 ## I
 
-| 名称 | 关键字 | 简介          |
-| ---- | ------ | ------------- |
-| I    | KWI    | IIntroduction |
+| 名称      | 关键字   | 简介                       |
+| --------- | -------- | -------------------------- |
+| [if else] | 流程控制 | 根据条件执行不同的代码分支 |
+| [else if] | 流程控制 | 处理多重条件               |
+| I         | KWI      | IIntroduction              |
+
+[if else]: https://course.rs/basic/flow-control.html#使用-if-来做分支控制
+[else if]: https://course.rs/basic/flow-control.html#使用-else-if-来处理多重条件
 
 [back](#head)
 
@@ -164,14 +176,17 @@
 
 ## L
 
-| 名称      | 关键字   | 简介                          |
-| --------- | -------- | ----------------------------- |
-| [let]     | 变量绑定 | `let x : u32 = 5;`            |
-| [let mut] | 可变变量 | `let mut x : u32 = 5; x = 9;` |
-| L         | KWL      | LIntroduction                 |
+| 名称        | 关键字   | 简介                           |
+| ----------- | -------- | ------------------------------ |
+| [let]       | 变量绑定 | `let x : u32 = 5;`             |
+| [let mut]   | 可变变量 | `let mut x : u32 = 5; x = 9;`  |
+| [loop 循环] | 循环控制 | 无限循环,注意要配合 [`break`] |
+| L           | KWL      | LIntroduction                  |
 
 [let]: https://course.rs/basic/variable.html#变量绑定
 [let mut]: https://course.rs/basic/variable.html#变量可变性
+[`break`]: https://course.rs/basic/flow-control.html#break
+[loop 循环]: https://course.rs/basic/flow-control.html#loop-循环
 
 [back](#head)
 
@@ -287,9 +302,12 @@
 
 ## W
 
-| 名称 | 关键字 | 简介          |
-| ---- | ------ | ------------- |
-| W    | KWW    | WIntroduction |
+| 名称         | 关键字   | 简介                                                   |
+| ------------ | -------- | ------------------------------------------------------ |
+| [while 循环] | 循环控制 | 当条件为 `true` 时,继续循环,条件为 `false`,跳出循环 |
+| W            | KWW      | WIntroduction                                          |
+
+[while 循环]: https://course.rs/basic/flow-control.html#while-循环
 
 [back](#head)
 

From 7c397856c099f0edc3c62f78065930635f58b5e2 Mon Sep 17 00:00:00 2001
From: Allan Downey <AllanDowney@126.com>
Date: Sun, 3 Apr 2022 10:25:26 +0800
Subject: [PATCH 04/34] Update: unified format

---
 src/basic/flow-control.md | 32 +++++++++++++++-----------------
 1 file changed, 15 insertions(+), 17 deletions(-)

diff --git a/src/basic/flow-control.md b/src/basic/flow-control.md
index e5f24560..d17544bc 100644
--- a/src/basic/flow-control.md
+++ b/src/basic/flow-control.md
@@ -6,7 +6,7 @@
 
 ## 使用 if 来做分支控制
 
-> if else 无处不在 - 鲁迅
+> if else 无处不在 -- 鲁迅
 
 但凡你能找到一门编程语言没有 `if else`,那么一定更要反馈给鲁迅,反正不是我说的:) 总之,只要你拥有其它语言的编程经验,就一定会有以下认知:`if else` **表达式**根据条件执行不同的代码分支:
 
@@ -56,7 +56,7 @@ error[E0308]: if and else have incompatible types
              found type `&str`
 ```
 
-#### 使用 else if 来处理多重条件
+## 使用 else if 来处理多重条件
 
 可以将 `else if` 与 `if`、`else` 组合在一起实现更复杂的条件分支判断:
 
@@ -82,20 +82,20 @@ fn main() {
 
 如果代码中有大量的 `else if ` 会让代码变得极其丑陋,不过不用担心,下一章的 `match` 专门用以解决多分支模式匹配的问题。
 
-## 循环控制
+# 循环控制
 
 循环无处不在,上到数钱,下到数年,你能想象的很多场景都存在循环,因此它也是流程控制中最重要的组成部分之一。
 
 在 Rust 语言中有三种循环方式:`for`、`while` 和 `loop`,其中 `for` 循环是 Rust 循环王冠上的明珠。
 
-#### for 循环
+## for 循环
 
 `for` 循环是 Rust 的大杀器:
 
 ```rust
 fn main() {
     for i in 1..=5 {
-        println!("{}",i);
+        println!("{}", i);
     }
 }
 ```
@@ -140,10 +140,10 @@ for item in &mut collection {
 
 ```rust
 fn main() {
-    let a = [4,3,2,1];
+    let a = [4, 3, 2, 1];
     // `.iter()` 方法把 `a` 数组变成一个迭代器
-    for (i,v) in a.iter().enumerate() {
-        println!("第{}个元素是{}",i+1,v);
+    for (i, v) in a.iter().enumerate() {
+        println!("第{}个元素是{}", i + 1, v);
     }
 }
 ```
@@ -183,7 +183,7 @@ for item in collection {
 
 由于 `for` 循环无需任何条件限制,也不需要通过索引来访问,因此是最安全也是最常用的,通过与下面的 `while` 的对比,我们能看到为什么 `for` 会更加安全。
 
-#### `continue`
+## `continue`
 
 使用 `continue` 可以跳过当前当次的循环,开始下次的循环:
 
@@ -192,7 +192,7 @@ for item in collection {
      if i == 2 {
          continue;
      }
-     println!("{}",i);
+     println!("{}", i);
  }
 ```
 
@@ -203,7 +203,7 @@ for item in collection {
 3
 ```
 
-#### `break`
+## `break`
 
 使用 `break` 可以直接跳出当前整个循环:
 
@@ -212,7 +212,7 @@ for item in collection {
      if i == 2 {
          break;
      }
-     println!("{}",i);
+     println!("{}", i);
  }
 ```
 
@@ -222,7 +222,7 @@ for item in collection {
 1
 ```
 
-#### while 循环
+## while 循环
 
 如果你需要一个条件来循环,当该条件为 `true` 时,继续循环,条件为 `false`,跳出循环,那么 `while` 就非常适用:
 
@@ -262,7 +262,7 @@ fn main() {
         if n > 5 {
             break
         }
-        println!("{}",n);
+        println!("{}", n);
         n+=1;
     }
 
@@ -317,7 +317,7 @@ fn main() {
 
 可以看出,`for` 并不会使用索引去访问数组,因此更安全也更简洁,同时避免 `运行时的边界检查`,性能更高。
 
-#### loop 循环
+## loop 循环
 
 对于循环而言,`loop` 循环毋庸置疑,是适用面最高的,它可以适用于所有循环场景(虽然能用,但是在很多场景下, `for` 和 `while` 才是最优选择),因为 `loop` 就是一个简单的无限循环,你可以在内部实现逻辑通过 `break` 关键字来控制循环何时结束。
 
@@ -368,8 +368,6 @@ fn main() {
 - **break 可以单独使用,也可以带一个返回值**,有些类似 `return`
 - **loop 是一个表达式**,因此可以返回一个值
 
-
 ## 课后练习
 
 > [Rust By Practice](https://zh.practice.rs/flow-control.html),支持代码在线编辑和运行,并提供详细的[习题解答](https://github.com/sunface/rust-by-practice)。
-

From 93e73e68669f8e5cbccbfac71c94000f3fb8c9a0 Mon Sep 17 00:00:00 2001
From: sunface <cto@188.com>
Date: Sun, 3 Apr 2022 22:56:46 +0800
Subject: [PATCH 05/34] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AB=A0=E8=8A=82=20[C?=
 =?UTF-8?q?ookbook=20-=20=E7=BA=BF=E7=A8=8B]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/SUMMARY.md                     |   9 +-
 src/cookbook/cocurrency/threads.md | 336 +++++++++++++++++++++++++++++
 src/cookbook/compression/intro.md  |   3 -
 3 files changed, 342 insertions(+), 6 deletions(-)
 create mode 100644 src/cookbook/cocurrency/threads.md
 delete mode 100644 src/cookbook/compression/intro.md

diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index 89e1ba93..41a95322 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -270,9 +270,12 @@
   - [命令行]()
     - [参数解析](cookbook/cmd/parsing.md)
     - [终端输出格式化](cookbook/cmd/ansi.md)
-  - [压缩](cookbook/compression/intro.md)
+  - [压缩]()
     - [使用.tar包](cookbook/compression/tar.md)
-  - [配置文件解析 todo](cookbook/config.md)
+  - [并发]()
+    - [线程](cookbook/cocurrency/threads.md)
+
+  <!-- - [配置文件解析 todo](cookbook/config.md)
   - [编解码 todo](cookbook/encoding/intro.md)
     - [JSON](cookbook/encoding/json.md)
     - [CSV](cookbook/encoding/csv.md)
@@ -291,7 +294,7 @@
   - [时间日期](cookbook/date.md)
   - [开发调试 todo](cookbook/dev/intro.md)
     - [日志](cookbook/dev/logs.md)
-    - [性能分析](cookbook/dev/profile.md)
+    - [性能分析](cookbook/dev/profile.md) -->
 
 <!--
 - [Rust区块链入门]()
diff --git a/src/cookbook/cocurrency/threads.md b/src/cookbook/cocurrency/threads.md
new file mode 100644
index 00000000..b980f9ca
--- /dev/null
+++ b/src/cookbook/cocurrency/threads.md
@@ -0,0 +1,336 @@
+# 线程
+
+### 生成一个临时性的线程
+
+下面例子用到了 [crossbeam](cookbook/cocurrency/intro.md) 包,它提供了非常实用的、用于并发和并行编程的数据结构和函数。
+
+[Scope::spawn](https://docs.rs/crossbeam/*/crossbeam/thread/struct.Scope.html#method.spawn) 会生成一个被限定了作用域的线程,该线程最大的特点就是:它会在传给 [crossbeam::scope](https://docs.rs/crossbeam/0.8.1/crossbeam/fn.scope.html) 的闭包函数返回前先行结束。得益于这个特点,子线程的创建使用就像是本地闭包函数调用,因此生成的线程内部可以使用外部环境中的变量!
+
+
+```rust,editable
+fn main() {
+    let arr = &[1, 25, -4, 10];
+    let max = find_max(arr);
+    assert_eq!(max, Some(25));
+}
+
+// 将数组分成两个部分,并使用新的线程对它们进行处理
+fn find_max(arr: &[i32]) -> Option<i32> {
+    const THRESHOLD: usize = 2;
+  
+    if arr.len() <= THRESHOLD {
+        return arr.iter().cloned().max();
+    }
+
+    let mid = arr.len() / 2;
+    let (left, right) = arr.split_at(mid);
+  
+    crossbeam::scope(|s| {
+        let thread_l = s.spawn(|_| find_max(left));
+        let thread_r = s.spawn(|_| find_max(right));
+  
+        let max_l = thread_l.join().unwrap()?;
+        let max_r = thread_r.join().unwrap()?;
+  
+        Some(max_l.max(max_r))
+    }).unwrap()
+}
+```
+
+### 创建并行流水线
+下面我们使用 [crossbeam](https://docs.rs/crossbeam/latest/crossbeam/) 和 [crossbeam-channel](https://docs.rs/crossbeam-channel/*/crossbeam_channel/index.html) 来创建一个并行流水线:流水线的两端分别是数据源和数据下沉( sink ),在流水线中间,有两个工作线程会从源头接收数据,对数据进行并行处理,最后将数据下沉。
+
+- 消息通道( channel )是 [crossbeam_channel::bounded](https://docs.rs/crossbeam-channel/0.5.4/crossbeam_channel/fn.bounded.html),它只能缓存一条消息。当缓存满后,发送者继续调用 [crossbeam_channel::Sender::send] 发送消息时会阻塞,直到一个工作线程( 消费者 ) 拿走这条消息
+- 消费者获取消息时先到先得的策略,因此两个工作线程只有一个能取到消息,保证消息不会被重复消费、处理
+- 通过迭代器 [crossbeam_channel::Receiver::iter](https://docs.rs/crossbeam-channel/*/crossbeam_channel/struct.Receiver.html#method.iter) 读取消息会阻塞当前线程,直到新消息的到来或 channel 关闭
+- channel 只有在所有的发送者或消费者关闭后,才能被关闭。而其中一个消费者 `rcv2` 处于阻塞读取状态,无比被关闭,因此我们必须要关闭所有发送者: `drop(snd1);`  `drop(snd2)` ,这样 channel 关闭后,主线程的 `rcv2` 才能从阻塞状态退出,最后整个程序结束。大家还是迷惑的话,可以看看这篇[文章](https://course.rs/practice/pitfalls/main-with-channel-blocked.html)。
+
+```rust,editable
+extern crate crossbeam;
+extern crate crossbeam_channel;
+
+use std::thread;
+use std::time::Duration;
+use crossbeam_channel::bounded;
+
+fn main() {
+    let (snd1, rcv1) = bounded(1);
+    let (snd2, rcv2) = bounded(1);
+    let n_msgs = 4;
+    let n_workers = 2;
+
+    crossbeam::scope(|s| {
+        // 生产者线程
+        s.spawn(|_| {
+            for i in 0..n_msgs {
+                snd1.send(i).unwrap();
+                println!("Source sent {}", i);
+            }
+ 
+            // 关闭其中一个发送者 snd1
+            // 该关闭操作对于结束最后的循环是必须的
+            drop(snd1);
+        });
+
+        // 通过两个线程并行处理
+        for _ in 0..n_workers {
+            // 从数据源接收数据,然后发送到下沉端
+            let (sendr, recvr) = (snd2.clone(), rcv1.clone());
+            // 生成单独的工作线程
+            s.spawn(move |_| {
+            thread::sleep(Duration::from_millis(500));
+                // 等待通道的关闭
+                for msg in recvr.iter() {
+                    println!("Worker {:?} received {}.",
+                             thread::current().id(), msg);
+                    sendr.send(msg * 2).unwrap();
+                }
+            });
+        }
+        // 关闭通道,如果不关闭,下沉端将永远无法结束循环
+        drop(snd2);
+
+        // 下沉端
+        for msg in rcv2.iter() {
+            println!("Sink received {}", msg);
+        }
+    }).unwrap();
+}
+```
+
+
+### 线程间传递数据
+
+下面我们来看看 [crossbeam-channel](https://docs.rs/crossbeam-channel/*/crossbeam_channel/index.html) 的单生产者单消费者( SPSC ) 使用场景。
+
+```rust,editable
+use std::{thread, time};
+use crossbeam_channel::unbounded;
+
+fn main() {
+    // unbounded 意味着 channel 可以存储任意多的消息
+    let (snd, rcv) = unbounded();
+    let n_msgs = 5;
+    crossbeam::scope(|s| {
+        s.spawn(|_| {
+            for i in 0..n_msgs {
+                snd.send(i).unwrap();
+                thread::sleep(time::Duration::from_millis(100));
+            }
+        });
+    }).unwrap();
+    for _ in 0..n_msgs {
+        let msg = rcv.recv().unwrap();
+        println!("Received {}", msg);
+    }
+}
+```
+
+### 维护全局可变的状态
+
+[lazy_static]() 会创建一个全局的静态引用( static ref ),该引用使用了 `Mutex` 以支持可变性,因此我们可以在代码中对其进行修改。`Mutex` 能保证该全局状态同时只能被一个线程所访问。
+
+```rust,editable
+use error_chain::error_chain;
+use lazy_static::lazy_static;
+use std::sync::Mutex;
+
+error_chain!{ }
+
+lazy_static! {
+    static ref FRUIT: Mutex<Vec<String>> = Mutex::new(Vec::new());
+}
+
+fn insert(fruit: &str) -> Result<()> {
+    let mut db = FRUIT.lock().map_err(|_| "Failed to acquire MutexGuard")?;
+    db.push(fruit.to_string());
+    Ok(())
+}
+
+fn main() -> Result<()> {
+    insert("apple")?;
+    insert("orange")?;
+    insert("peach")?;
+    {
+        let db = FRUIT.lock().map_err(|_| "Failed to acquire MutexGuard")?;
+
+        db.iter().enumerate().for_each(|(i, item)| println!("{}: {}", i, item));
+    }
+    insert("grape")?;
+    Ok(())
+}
+```
+
+### 并行计算 iso 文件的 SHA256
+
+下面的示例将为当前目录中的每一个 .iso 文件都计算一个 SHA256 sum。其中线程池中会初始化和 CPU 核心数一致的线程数,其中核心数是通过 [num_cpus::get](https://docs.rs/num_cpus/*/num_cpus/fn.get.html) 函数获取。
+
+`Walkdir::new` 可以遍历当前的目录,然后调用 `execute` 来执行读操作和 SHA256 哈希计算。
+
+```rust,editable
+
+use walkdir::WalkDir;
+use std::fs::File;
+use std::io::{BufReader, Read, Error};
+use std::path::Path;
+use threadpool::ThreadPool;
+use std::sync::mpsc::channel;
+use ring::digest::{Context, Digest, SHA256};
+
+// Verify the iso extension
+fn is_iso(entry: &Path) -> bool {
+    match entry.extension() {
+        Some(e) if e.to_string_lossy().to_lowercase() == "iso" => true,
+        _ => false,
+    }
+}
+
+fn compute_digest<P: AsRef<Path>>(filepath: P) -> Result<(Digest, P), Error> {
+    let mut buf_reader = BufReader::new(File::open(&filepath)?);
+    let mut context = Context::new(&SHA256);
+    let mut buffer = [0; 1024];
+
+    loop {
+        let count = buf_reader.read(&mut buffer)?;
+        if count == 0 {
+            break;
+        }
+        context.update(&buffer[..count]);
+    }
+
+    Ok((context.finish(), filepath))
+}
+
+fn main() -> Result<(), Error> {
+    let pool = ThreadPool::new(num_cpus::get());
+
+    let (tx, rx) = channel();
+
+    for entry in WalkDir::new("/home/user/Downloads")
+        .follow_links(true)
+        .into_iter()
+        .filter_map(|e| e.ok())
+        .filter(|e| !e.path().is_dir() && is_iso(e.path())) {
+            let path = entry.path().to_owned();
+            let tx = tx.clone();
+            pool.execute(move || {
+                let digest = compute_digest(path);
+                tx.send(digest).expect("Could not send data!");
+            });
+        }
+
+    drop(tx);
+    for t in rx.iter() {
+        let (sha, path) = t?;
+        println!("{:?} {:?}", sha, path);
+    }
+    Ok(())
+}
+```
+
+
+### 使用线程池来绘制分形
+
+下面例子中将基于 [Julia Set]() 来绘制一个分形图片,其中使用到了线程池来做分布式计算。
+
+<img src="https://cloud.githubusercontent.com/assets/221000/26546700/9be34e80-446b-11e7-81dc-dd9871614ea1.png" />
+
+```rust,edtiable
+# use error_chain::error_chain;
+use std::sync::mpsc::{channel, RecvError};
+use threadpool::ThreadPool;
+use num::complex::Complex;
+use image::{ImageBuffer, Pixel, Rgb};
+
+#
+# error_chain! {
+#     foreign_links {
+#         MpscRecv(RecvError);
+#         Io(std::io::Error);
+#     }
+# }
+#
+# // Function converting intensity values to RGB
+# // Based on http://www.efg2.com/Lab/ScienceAndEngineering/Spectra.htm
+# fn wavelength_to_rgb(wavelength: u32) -> Rgb<u8> {
+#     let wave = wavelength as f32;
+#
+#     let (r, g, b) = match wavelength {
+#         380..=439 => ((440. - wave) / (440. - 380.), 0.0, 1.0),
+#         440..=489 => (0.0, (wave - 440.) / (490. - 440.), 1.0),
+#         490..=509 => (0.0, 1.0, (510. - wave) / (510. - 490.)),
+#         510..=579 => ((wave - 510.) / (580. - 510.), 1.0, 0.0),
+#         580..=644 => (1.0, (645. - wave) / (645. - 580.), 0.0),
+#         645..=780 => (1.0, 0.0, 0.0),
+#         _ => (0.0, 0.0, 0.0),
+#     };
+#
+#     let factor = match wavelength {
+#         380..=419 => 0.3 + 0.7 * (wave - 380.) / (420. - 380.),
+#         701..=780 => 0.3 + 0.7 * (780. - wave) / (780. - 700.),
+#         _ => 1.0,
+#     };
+#
+#     let (r, g, b) = (normalize(r, factor), normalize(g, factor), normalize(b, factor));
+#     Rgb::from_channels(r, g, b, 0)
+# }
+#
+# // Maps Julia set distance estimation to intensity values
+# fn julia(c: Complex<f32>, x: u32, y: u32, width: u32, height: u32, max_iter: u32) -> u32 {
+#     let width = width as f32;
+#     let height = height as f32;
+#
+#     let mut z = Complex {
+#         // scale and translate the point to image coordinates
+#         re: 3.0 * (x as f32 - 0.5 * width) / width,
+#         im: 2.0 * (y as f32 - 0.5 * height) / height,
+#     };
+#
+#     let mut i = 0;
+#     for t in 0..max_iter {
+#         if z.norm() >= 2.0 {
+#             break;
+#         }
+#         z = z * z + c;
+#         i = t;
+#     }
+#     i
+# }
+#
+# // Normalizes color intensity values within RGB range
+# fn normalize(color: f32, factor: f32) -> u8 {
+#     ((color * factor).powf(0.8) * 255.) as u8
+# }
+
+fn main() -> Result<()> {
+    let (width, height) = (1920, 1080);
+    // 为指定宽高的输出图片分配内存
+    let mut img = ImageBuffer::new(width, height);
+    let iterations = 300;
+
+    let c = Complex::new(-0.8, 0.156);
+
+    let pool = ThreadPool::new(num_cpus::get());
+    let (tx, rx) = channel();
+
+    for y in 0..height {
+        let tx = tx.clone();
+        // execute 将每个像素作为单独的作业接收
+        pool.execute(move || for x in 0..width {
+                         let i = julia(c, x, y, width, height, iterations);
+                         let pixel = wavelength_to_rgb(380 + i * 400 / iterations);
+                         tx.send((x, y, pixel)).expect("Could not send data!");
+                     });
+    }
+
+    for _ in 0..(width * height) {
+        let (x, y, pixel) = rx.recv()?;
+        // 使用数据来设置像素的颜色
+        img.put_pixel(x, y, pixel);
+    }
+    
+    // 输出图片内容到指定文件中
+    let _ = img.save("output.png")?;
+    Ok(())
+}
+```
\ No newline at end of file
diff --git a/src/cookbook/compression/intro.md b/src/cookbook/compression/intro.md
deleted file mode 100644
index aa2d4d8b..00000000
--- a/src/cookbook/compression/intro.md
+++ /dev/null
@@ -1,3 +0,0 @@
-## 压缩
-
-我们会对常用的压缩方法进行介绍,例如 `tar`, `gzip`, `lz4` 等。
\ No newline at end of file

From d606e8c9b0f21d66e855575edffc4b1f0bcb72c9 Mon Sep 17 00:00:00 2001
From: sunface <cto@188.com>
Date: Sun, 3 Apr 2022 22:57:23 +0800
Subject: [PATCH 06/34] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AB=A0=E8=8A=82=20[C?=
 =?UTF-8?q?ookbook=20-=20=E7=BA=BF=E7=A8=8B]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 内容变更记录.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/内容变更记录.md b/内容变更记录.md
index 866c0e73..fe542a52 100644
--- a/内容变更记录.md
+++ b/内容变更记录.md
@@ -1,6 +1,10 @@
 # ChangeLog
 记录一些值得注意的变更。
 
+## 2022-04-03
+
+- 新增章节:[Cookbook - 线程](http://course.rs/cookbook/cocurrency/threads.html)
+
 ## 2022-04-01
 
 - 新增章节: [Cookbook - 生成随机值](https://course.rs/cookbook/algos/randomness.html)

From 3cc023f64e985dd0548c98ffe178126274bdf167 Mon Sep 17 00:00:00 2001
From: Allan Downey <AllanDowney@126.com>
Date: Mon, 4 Apr 2022 03:25:54 +0800
Subject: [PATCH 07/34] update(index-list): add match and if let

---
 src/index-list.md | 71 +++++++++++++++++++++++++++--------------------
 1 file changed, 41 insertions(+), 30 deletions(-)

diff --git a/src/index-list.md b/src/index-list.md
index f50acd14..d4ae7d59 100644
--- a/src/index-list.md
+++ b/src/index-list.md
@@ -18,18 +18,18 @@
 
 ## Sym
 
-| 名称                    | 关键字         | 简介                                             |
-| ----------------------- | -------------- | ------------------------------------------------ |
-| [?]                     | 错误传播       | 用于简化错误传播                                 |
-| [()]                    | 单元类型       | 单元类型,无返回值                               |
-| `!` : [1 函数] [2 类型] | 永不返回       | 永不返回                                         |
-| [&]                     | 引用           | 常规引用是一个指针类型,指向了对象存储的内存地址 |
-| [\*]                    | 解引用         | 解出引用所指向的值                               |
-| [@]                     | 变量绑定       | 为一个字段绑定另外一个变量                       |
-| `_`                     |                | 忽略该值或者类型                                 |
-| ['a: 'b]                | 生命周期约束   | 用来说明两个生命周期的长短                       |
-| [{:?}] {:#?}            | 打印结构体信息 | 使用 `#[derive(Debug)]` 派生实现 `Debug` 特征    |
-| A                       |                | AIntroduction                                    |
+| 名称                    | 关键字         | 简介                                                                                 |
+| ----------------------- | -------------- | ------------------------------------------------------------------------------------ |
+| [?]                     | 错误传播       | 用于简化错误传播                                                                     |
+| [()]                    | 单元类型       | 单元类型,无返回值                                                                   |
+| `!` : [1 函数] [2 类型] | 永不返回       | 永不返回                                                                             |
+| [&]                     | 引用           | 常规引用是一个指针类型,指向了对象存储的内存地址                                     |
+| [\*]                    | 解引用         | 解出引用所指向的值                                                                   |
+| [@]                     | 变量绑定       | 为一个字段绑定另外一个变量                                                           |
+| `_` : [2 模式匹配]      | 忽略           | 1. 忽略该值或者类型,否则编译器会给你一个 `变量未使用的` 的警告<br>2. 模式匹配通配符 |
+| ['a: 'b]                | 生命周期约束   | 用来说明两个生命周期的长短                                                           |
+| [{:?}] {:#?}            | 打印结构体信息 | 使用 `#[derive(Debug)]` 派生实现 `Debug` 特征                                        |
+| A                       |                | AIntroduction                                                                        |
 
 [?]: https://course.rs/basic/result-error/result.html#传播界的大明星-
 [()]: https://course.rs/basic/base-type/function.html#无返回值
@@ -40,6 +40,7 @@
 [@]: https://course.rs/basic/match-pattern/all-patterns.html#绑定
 ['a: 'b]: https://course.rs/advance/lifetime/advance.html#生命周期约束-hrtb
 [{:?}]: https://course.rs/basic/compound-type/struct.html?search=#使用-derivedebug-来打印结构体的信息
+[2 模式匹配]: https://course.rs/basic/match-pattern/match-if-let.html#_-通配符
 
 [back](#head)
 
@@ -58,16 +59,18 @@
 
 ## B
 
-| 名称         | 关键字    | 简介                                   |
-| ------------ | --------- | -------------------------------------- |
-| [变量遮蔽]   | shadowing | 允许声明相同的变量名,后者会遮蔽掉前者 |
-| [变量作用域] | 所有权    | 作用域是一个变量在程序中有效的范围     |
-| [表达式]     |           | 进行求值,结尾无 `;`,有返回值         |
-| [bool 布尔]  | 布尔类型  | `true` `false`,占用 1 字节            |
-| [break]      | 循环控制  | 直接跳出当前整个循环                   |
-| B            | KWB       | BIntroduction                          |
+| 名称         | 关键字    | 简介                                                                           |
+| ------------ | --------- | ------------------------------------------------------------------------------ |
+| [变量遮蔽]   | shadowing | 允许声明相同的变量名,后者会遮蔽掉前者                                         |
+| [变量覆盖]   | 模式匹配  | 无论是是 `match` 还是 `if let`,他们都可以在模式匹配时覆盖掉老的值,绑定新的值 |
+| [变量作用域] | 所有权    | 作用域是一个变量在程序中有效的范围                                             |
+| [表达式]     |           | 进行求值,结尾无 `;`,有返回值                                                 |
+| [bool 布尔]  | 布尔类型  | `true` `false`,占用 1 字节                                                    |
+| [break]      | 循环控制  | 直接跳出当前整个循环                                                           |
+| B            | KWB       | BIntroduction                                                                  |
 
 [变量遮蔽]: https://course.rs/basic/variable.html#变量遮蔽shadowing
+[变量覆盖]: https://course.rs/basic/match-pattern/match-if-let.html#变量覆盖
 [变量作用域]: https://course.rs/basic/ownership/ownership.html#变量作用域
 [bool 布尔]: https://course.rs/basic/base-type/char-bool.html#布尔bool
 [表达式]: https://course.rs/basic/base-type/statement-expression.html#表达式
@@ -147,14 +150,16 @@
 
 ## I
 
-| 名称      | 关键字   | 简介                       |
-| --------- | -------- | -------------------------- |
-| [if else] | 流程控制 | 根据条件执行不同的代码分支 |
-| [else if] | 流程控制 | 处理多重条件               |
-| I         | KWI      | IIntroduction              |
+| 名称          | 关键字   | 简介                                                                  |
+| ------------- | -------- | --------------------------------------------------------------------- |
+| [if else]     | 流程控制 | 根据条件执行不同的代码分支                                            |
+| [else if]     | 流程控制 | 处理多重条件                                                          |
+| [if let 匹配] | 模式匹配 | 当你只要匹配一个条件,且忽略其他条件时就用 `if let`,否则都用 `match` |
+| I             | KWI      | IIntroduction                                                         |
 
 [if else]: https://course.rs/basic/flow-control.html#使用-if-来做分支控制
 [else if]: https://course.rs/basic/flow-control.html#使用-else-if-来处理多重条件
+[if let 匹配]: https://course.rs/basic/match-pattern/match-if-let.html#if-let-匹配
 
 [back](#head)
 
@@ -192,11 +197,17 @@
 
 ## M
 
-| 名称        | 关键字     | 简介                                                  |
-| ----------- | ---------- | ----------------------------------------------------- |
-| [move 移动] | 转移所有权 | `let s2 = s1;`<br>`s1` 所有权转移给了 `s2`,`s1` 失效 |
-| M           | KWM        | MIntroduction                                         |
-
+| 名称          | 关键字     | 简介                                                                                                                                                               |
+| ------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| [模式绑定]    | 模式匹配   | 从模式中取出绑定的值                                                                                                                                               |
+| [match 匹配]  | 模式匹配   | `match` 的匹配必须要穷举出所有可能,因此这里用 `_ ` 来代表未列出的所有可能性<br>`match` 的每一个分支都必须是一个表达式,且所有分支的表达式最终返回值的类型必须相同 |
+| [matches! 宏] | 模式匹配   | 将一个表达式跟模式进行匹配,然后返回匹配的结果 `true` 或 `false`                                                                                                   |
+| [move 移动]   | 转移所有权 | `let s2 = s1;`<br>`s1` 所有权转移给了 `s2`,`s1` 失效                                                                                                              |
+| M             | KWM        | MIntroduction                                                                                                                                                      |
+
+[模式绑定]: https://course.rs/basic/match-pattern/match-if-let.html#模式绑定
+[match 匹配]: https://course.rs/basic/match-pattern/match-if-let.html#match-匹配
+[matches! 宏]: https://course.rs/basic/match-pattern/match-if-let.html#matches宏
 [move 移动]: https://course.rs/basic/ownership/ownership.html#转移所有权
 
 [back](#head)

From 3d24f52703a0074e38bebefc4d0110e6d5df3304 Mon Sep 17 00:00:00 2001
From: Allan Downey <AllanDowney@126.com>
Date: Mon, 4 Apr 2022 03:39:20 +0800
Subject: [PATCH 08/34] update(index-list): add match Option(T)

---
 src/index-list.md | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/src/index-list.md b/src/index-list.md
index d4ae7d59..5bc36313 100644
--- a/src/index-list.md
+++ b/src/index-list.md
@@ -222,12 +222,14 @@
 
 ## O
 
-| 名称     | 关键字      | 简介          |
-| -------- | ----------- | ------------- |
-| [Option] | Option 枚举 | 用于处理空值  |
-| O        | KWO         | OIntroduction |
+| 名称          | 关键字      | 简介                                                            |
+| ------------- | ----------- | --------------------------------------------------------------- |
+| [Option]      | Option 枚举 | 用于处理空值,**一个变量要么有值:`Some(T)`, 要么为空:`None`** |
+| [Option 解构] | 模式匹配    | 可以通过 `match` 来实现                                         |
+| O             | KWO         | OIntroduction                                                   |
 
 [option]: https://course.rs/basic/compound-type/enum.html#option-枚举用于处理空值
+[option 解构]: https://course.rs/basic/match-pattern/option.html#匹配-optiont
 
 [back](#head)
 

From ddbd558611cbe02c8340047f5081ad5d6dbdd8ec Mon Sep 17 00:00:00 2001
From: sunface <cto@188.com>
Date: Mon, 4 Apr 2022 12:47:33 +0800
Subject: [PATCH 09/34] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AB=A0=E8=8A=82=20[C?=
 =?UTF-8?q?ookbook=20-=20rayon]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/SUMMARY.md                      |   2 +-
 src/cookbook/cocurrency/parallel.md | 201 ++++++++++++++++++++++++++++
 2 files changed, 202 insertions(+), 1 deletion(-)
 create mode 100644 src/cookbook/cocurrency/parallel.md

diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index 9b582c75..798f3074 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -274,7 +274,7 @@
     - [使用.tar包](cookbook/compression/tar.md)
   - [并发]()
     - [线程](cookbook/cocurrency/threads.md)
-
+    - [使用rayon并行处理数据](cookbook/cocurrency/parallel.md)
   <!-- - [配置文件解析 todo](cookbook/config.md)
   - [编解码 todo](cookbook/encoding/intro.md)
     - [JSON](cookbook/encoding/json.md)
diff --git a/src/cookbook/cocurrency/parallel.md b/src/cookbook/cocurrency/parallel.md
new file mode 100644
index 00000000..d1d5c521
--- /dev/null
+++ b/src/cookbook/cocurrency/parallel.md
@@ -0,0 +1,201 @@
+# 任务并行处理
+
+### 并行修改数组中的元素
+
+[rayon](https://docs.rs/rayon/1.5.1/rayon/index.html) 提供了一个 [par_iter_mut](https://docs.rs/rayon/*/rayon/iter/trait.IntoParallelRefMutIterator.html#tymethod.par_iter_mut) 方法用于并行化迭代一个数据集合。
+
+```rust,editable
+use rayon::prelude::*;
+
+fn main() {
+    let mut arr = [0, 7, 9, 11];
+    arr.par_iter_mut().for_each(|p| *p -= 1);
+    println!("{:?}", arr);
+}
+```
+
+### 并行测试集合中的元素是否满足给定的条件
+
+[rayon::any](https://docs.rs/rayon/*/rayon/iter/trait.ParallelIterator.html#method.any) 和 [rayon::all](https://docs.rs/rayon/*/rayon/iter/trait.ParallelIterator.html#method.all) 类似于 [std::any](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.any) / [std::all](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.all) ,但是是并行版本的。
+
+- `rayon::any` 并行检查迭代器中是否有任何元素满足给定的条件,一旦发现符合条件的元素,就立即返回
+- `rayon::all` 并行检查迭代器中的所有元素是否满足给定的条件,一旦发现不满足条件的元素,就立即返回
+
+```rust,editable
+use rayon::prelude::*;
+
+fn main() {
+    let mut vec = vec![2, 4, 6, 8];
+
+    assert!(!vec.par_iter().any(|n| (*n % 2) != 0));
+    assert!(vec.par_iter().all(|n| (*n % 2) == 0));
+    assert!(!vec.par_iter().any(|n| *n > 8 ));
+    assert!(vec.par_iter().all(|n| *n <= 8 ));
+
+    vec.push(9);
+
+    assert!(vec.par_iter().any(|n| (*n % 2) != 0));
+    assert!(!vec.par_iter().all(|n| (*n % 2) == 0));
+    assert!(vec.par_iter().any(|n| *n > 8 ));
+    assert!(!vec.par_iter().all(|n| *n <= 8 )); 
+}
+```
+
+### 使用给定条件并行搜索
+下面例子使用 [par_iter](https://docs.rs/rayon/*/rayon/iter/trait.IntoParallelRefIterator.html#tymethod.par_iter) 和 [rayon::find_any](https://docs.rs/rayon/*/rayon/iter/trait.ParallelIterator.html#method.find_any) 来并行搜索一个数组,直到找到任意一个满足条件的元素。
+
+如果有多个元素满足条件,`rayon` 会返回第一个找到的元素,注意:第一个找到的元素未必是数组中的顺序最靠前的那个。
+
+```rust,editable
+use rayon::prelude::*;
+
+fn main() {
+    let v = vec![6, 2, 1, 9, 3, 8, 11];
+
+    // 这里使用了 `&&x` 的形式,大家可以在以下链接阅读更多 https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.find
+    let f1 = v.par_iter().find_any(|&&x| x == 9);
+    let f2 = v.par_iter().find_any(|&&x| x % 2 == 0 && x > 6);
+    let f3 = v.par_iter().find_any(|&&x| x > 8);
+
+    assert_eq!(f1, Some(&9));
+    assert_eq!(f2, Some(&8));
+    assert!(f3 > Some(&8));
+}
+```
+
+### 对数组进行并行排序
+下面的例子将对字符串数组进行并行排序。
+
+[par_sort_unstable](https://docs.rs/rayon/*/rayon/slice/trait.ParallelSliceMut.html#method.par_sort_unstable) 方法的排序性能往往要比[稳定的排序算法](https://docs.rs/rayon/1.5.1/rayon/slice/trait.ParallelSliceMut.html#method.par_sort)更高。
+
+
+```rust,editable
+use rand::{Rng, thread_rng};
+use rand::distributions::Alphanumeric;
+use rayon::prelude::*;
+
+fn main() {
+  let mut vec = vec![String::new(); 100_000];
+  // 并行生成数组中的字符串
+  vec.par_iter_mut().for_each(|p| {
+    let mut rng = thread_rng();
+    *p = (0..5).map(|_| rng.sample(&Alphanumeric)).collect()
+  });
+  
+  // 
+  vec.par_sort_unstable();
+}
+```
+
+### 并行化 Map-Reuduce
+
+下面例子使用 [rayon::filter](https://docs.rs/rayon/*/rayon/iter/trait.ParallelIterator.html#method.filter), [rayon::map](https://docs.rs/rayon/*/rayon/iter/trait.ParallelIterator.html#method.map), 和 [rayon::reduce](https://docs.rs/rayon/*/rayon/iter/trait.ParallelIterator.html#method.reduce) 来超过 30 岁的 `Person` 的平均年龄。
+
+- `rayon::filter` 返回集合中所有满足给定条件的元素
+- `rayon::map` 对集合中的每一个元素执行一个操作,创建并返回新的迭代器,类似于[迭代器适配器](https://course.rs/advance/functional-programing/iterator.html#迭代器适配器)
+- `rayon::reduce` 则迭代器的元素进行不停的聚合运算,直到获取一个最终结果,这个结果跟例子中 `rayon::sum` 获取的结果是相同的
+
+```rust,editable
+use rayon::prelude::*;
+
+struct Person {
+    age: u32,
+}
+
+fn main() {
+    let v: Vec<Person> = vec![
+        Person { age: 23 },
+        Person { age: 19 },
+        Person { age: 42 },
+        Person { age: 17 },
+        Person { age: 17 },
+        Person { age: 31 },
+        Person { age: 30 },
+    ];
+
+    let num_over_30 = v.par_iter().filter(|&x| x.age > 30).count() as f32;
+    let sum_over_30 = v.par_iter()
+        .map(|x| x.age)
+        .filter(|&x| x > 30)
+        .reduce(|| 0, |x, y| x + y);
+
+    let alt_sum_30: u32 = v.par_iter()
+        .map(|x| x.age)
+        .filter(|&x| x > 30)
+        .sum();
+
+    let avg_over_30 = sum_over_30 as f32 / num_over_30;
+    let alt_avg_over_30 = alt_sum_30 as f32/ num_over_30;
+
+    assert!((avg_over_30 - alt_avg_over_30).abs() < std::f32::EPSILON);
+    println!("The average age of people older than 30 is {}", avg_over_30);
+}
+```
+
+### 并行生成缩略图
+下面例子将为目录中的所有图片并行生成缩略图,然后将结果存到新的目录 `thumbnails` 中。
+
+[glob::glob_with](https://docs.rs/glob/*/glob/fn.glob_with.html) 可以找出当前目录下的所有 `.jpg` 文件,`rayon` 通过 [DynamicImage::resize](https://docs.rs/image/*/image/enum.DynamicImage.html#method.resize) 来并行调整图片的大小。
+
+```rust,editable
+# use error_chain::error_chain;
+
+use std::path::Path;
+use std::fs::create_dir_all;
+
+# use error_chain::ChainedError;
+use glob::{glob_with, MatchOptions};
+use image::{FilterType, ImageError};
+use rayon::prelude::*;
+
+# error_chain! {
+#    foreign_links {
+#        Image(ImageError);
+#        Io(std::io::Error);
+#        Glob(glob::PatternError);
+#    }
+#}
+
+fn main() -> Result<()> {
+    let options: MatchOptions = Default::default();
+    // 找到当前目录中的所有 `jpg` 文件
+    let files: Vec<_> = glob_with("*.jpg", options)?
+        .filter_map(|x| x.ok())
+        .collect();
+
+    if files.len() == 0 {
+        error_chain::bail!("No .jpg files found in current directory");
+    }
+
+    let thumb_dir = "thumbnails";
+    create_dir_all(thumb_dir)?;
+
+    println!("Saving {} thumbnails into '{}'...", files.len(), thumb_dir);
+
+    let image_failures: Vec<_> = files
+        .par_iter()
+        .map(|path| {
+            make_thumbnail(path, thumb_dir, 300)
+                .map_err(|e| e.chain_err(|| path.display().to_string()))
+        })
+        .filter_map(|x| x.err())
+        .collect();
+
+    image_failures.iter().for_each(|x| println!("{}", x.display_chain()));
+
+    println!("{} thumbnails saved successfully", files.len() - image_failures.len());
+    Ok(())
+}
+
+fn make_thumbnail<PA, PB>(original: PA, thumb_dir: PB, longest_edge: u32) -> Result<()>
+where
+    PA: AsRef<Path>,
+    PB: AsRef<Path>,
+{
+    let img = image::open(original.as_ref())?;
+    let file_path = thumb_dir.as_ref().join(original);
+
+    Ok(img.resize(longest_edge, longest_edge, FilterType::Nearest)
+        .save(file_path)?)
+}
+```
\ No newline at end of file

From 3d12bd434adeb68e0ad748bb707c75ac489e4046 Mon Sep 17 00:00:00 2001
From: sunface <cto@188.com>
Date: Mon, 4 Apr 2022 12:48:40 +0800
Subject: [PATCH 10/34] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AB=A0=E8=8A=82=20[C?=
 =?UTF-8?q?ookbook=20-=20rayon]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 内容变更记录.md | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/内容变更记录.md b/内容变更记录.md
index d1192fe0..4b301d2c 100644
--- a/内容变更记录.md
+++ b/内容变更记录.md
@@ -1,6 +1,11 @@
 # ChangeLog
 记录一些值得注意的变更。
 
+## 2022-04-04 
+
+- 新增章节: [Cookbook - 使用 rayon 并行处理数据](https://course.rs/cookbook/cocurrency/parallel.html)
+
+
 ## 2022-04-03
 
 - 新增章节:[Cookbook - 线程](http://course.rs/cookbook/cocurrency/threads.html)

From b7f873b5c9123bbad00eaf1c2613a60e681ce9a2 Mon Sep 17 00:00:00 2001
From: sunface <cto@188.com>
Date: Mon, 4 Apr 2022 14:39:25 +0800
Subject: [PATCH 11/34] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AB=A0=E8=8A=82=20[C?=
 =?UTF-8?q?ookbook=20-=20=E5=93=88=E5=B8=8C]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/SUMMARY.md                          |  9 ++++
 src/cookbook/cryptography/encryption.md |  1 +
 src/cookbook/cryptography/hashing.md    | 71 +++++++++++++++++++++++++
 src/cookbook/database/postgres.md       |  1 +
 src/cookbook/database/sqlite.md         |  1 +
 src/cookbook/datastructures/bitfield.md |  1 +
 6 files changed, 84 insertions(+)
 create mode 100644 src/cookbook/cryptography/encryption.md
 create mode 100644 src/cookbook/cryptography/hashing.md
 create mode 100644 src/cookbook/database/postgres.md
 create mode 100644 src/cookbook/database/sqlite.md
 create mode 100644 src/cookbook/datastructures/bitfield.md

diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index 798f3074..166218dd 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -275,6 +275,15 @@
   - [并发]()
     - [线程](cookbook/cocurrency/threads.md)
     - [使用rayon并行处理数据](cookbook/cocurrency/parallel.md)
+  - [密码学]()
+    - [哈希](cookbook/cryptography/hashing.md)
+    - [加密](cookbook/cryptography/encryption.md)
+  - [数据结构]()
+    - [位字段](cookbook/datastructures/bitfield.md)
+  - [数据库]()
+    - [SQLite](cookbook/database/sqlite.md)
+    - [Postgres](cookbook/database/postgres.md)
+
   <!-- - [配置文件解析 todo](cookbook/config.md)
   - [编解码 todo](cookbook/encoding/intro.md)
     - [JSON](cookbook/encoding/json.md)
diff --git a/src/cookbook/cryptography/encryption.md b/src/cookbook/cryptography/encryption.md
new file mode 100644
index 00000000..c3e253eb
--- /dev/null
+++ b/src/cookbook/cryptography/encryption.md
@@ -0,0 +1 @@
+# 加密
diff --git a/src/cookbook/cryptography/hashing.md b/src/cookbook/cryptography/hashing.md
new file mode 100644
index 00000000..60a697c0
--- /dev/null
+++ b/src/cookbook/cryptography/hashing.md
@@ -0,0 +1,71 @@
+# 哈希
+
+### 计算文件的 SHA-256 摘要
+写入一些数据到文件中,然后使用 [digest::Context](https://briansmith.org/rustdoc/ring/digest/struct.Context.html) 来计算文件内容的 SHA-256 摘要 [digest::Digest](https://briansmith.org/rustdoc/ring/digest/struct.Digest.html)。
+
+```rust,editable
+# use error_chain::error_chain;
+use data_encoding::HEXUPPER;
+use ring::digest::{Context, Digest, SHA256};
+use std::fs::File;
+use std::io::{BufReader, Read, Write};
+
+# error_chain! {
+#    foreign_links {
+#        Io(std::io::Error);
+#        Decode(data_encoding::DecodeError);
+#    }
+# }
+
+fn sha256_digest<R: Read>(mut reader: R) -> Result<Digest> {
+    let mut context = Context::new(&SHA256);
+    let mut buffer = [0; 1024];
+
+    loop {
+        let count = reader.read(&mut buffer)?;
+        if count == 0 {
+            break;
+        }
+        context.update(&buffer[..count]);
+    }
+
+    Ok(context.finish())
+}
+
+fn main() -> Result<()> {
+    let path = "file.txt";
+
+    let mut output = File::create(path)?;
+    write!(output, "We will generate a digest of this text")?;
+
+    let input = File::open(path)?;
+    let reader = BufReader::new(input);
+    let digest = sha256_digest(reader)?;
+
+    println!("SHA-256 digest is {}", HEXUPPER.encode(digest.as_ref()));
+
+    Ok(())
+}
+```
+
+### 使用 HMAC 摘要来签名和验证消息
+使用 [ring::hmac](https://briansmith.org/rustdoc/ring/hmac/) 创建一个字符串签名并检查该签名的正确性。
+
+```rust,editable
+use ring::{hmac, rand};
+use ring::rand::SecureRandom;
+use ring::error::Unspecified;
+
+fn main() -> Result<(), Unspecified> {
+    let mut key_value = [0u8; 48];
+    let rng = rand::SystemRandom::new();
+    rng.fill(&mut key_value)?;
+    let key = hmac::Key::new(hmac::HMAC_SHA256, &key_value);
+
+    let message = "Legitimate and important message.";
+    let signature = hmac::sign(&key, message.as_bytes());
+    hmac::verify(&key, message.as_bytes(), signature.as_ref())?;
+
+    Ok(())
+}
+```
\ No newline at end of file
diff --git a/src/cookbook/database/postgres.md b/src/cookbook/database/postgres.md
new file mode 100644
index 00000000..a7be4c31
--- /dev/null
+++ b/src/cookbook/database/postgres.md
@@ -0,0 +1 @@
+# Postgres
diff --git a/src/cookbook/database/sqlite.md b/src/cookbook/database/sqlite.md
new file mode 100644
index 00000000..02122687
--- /dev/null
+++ b/src/cookbook/database/sqlite.md
@@ -0,0 +1 @@
+# SQLite
diff --git a/src/cookbook/datastructures/bitfield.md b/src/cookbook/datastructures/bitfield.md
new file mode 100644
index 00000000..7f16f82f
--- /dev/null
+++ b/src/cookbook/datastructures/bitfield.md
@@ -0,0 +1 @@
+# 位字段

From 33e2dda9d3ce0744100934dd14cb0520fd286f52 Mon Sep 17 00:00:00 2001
From: sunface <cto@188.com>
Date: Mon, 4 Apr 2022 15:04:20 +0800
Subject: [PATCH 12/34] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AB=A0=E8=8A=82=20[C?=
 =?UTF-8?q?ookbook=20-=20=E5=8A=A0=E5=AF=86]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/cookbook/cryptography/encryption.md | 56 +++++++++++++++++++++++++
 1 file changed, 56 insertions(+)

diff --git a/src/cookbook/cryptography/encryption.md b/src/cookbook/cryptography/encryption.md
index c3e253eb..a728da46 100644
--- a/src/cookbook/cryptography/encryption.md
+++ b/src/cookbook/cryptography/encryption.md
@@ -1 +1,57 @@
 # 加密
+
+### 使用  PBKDF2 对密码进行哈希和加盐( salt )
+[ring::pbkdf2]() 可以对一个加盐密码进行哈希。
+
+```rust,editable
+
+use data_encoding::HEXUPPER;
+use ring::error::Unspecified;
+use ring::rand::SecureRandom;
+use ring::{digest, pbkdf2, rand};
+use std::num::NonZeroU32;
+
+fn main() -> Result<(), Unspecified> {
+    const CREDENTIAL_LEN: usize = digest::SHA512_OUTPUT_LEN;
+    let n_iter = NonZeroU32::new(100_000).unwrap();
+    let rng = rand::SystemRandom::new();
+
+    let mut salt = [0u8; CREDENTIAL_LEN];
+    // 生成 salt: 将安全生成的随机数填入到字节数组中
+    rng.fill(&mut salt)?;
+
+    let password = "Guess Me If You Can!";
+    let mut pbkdf2_hash = [0u8; CREDENTIAL_LEN];
+    pbkdf2::derive(
+        pbkdf2::PBKDF2_HMAC_SHA512,
+        n_iter,
+        &salt,
+        password.as_bytes(),
+        &mut pbkdf2_hash,
+    );
+    println!("Salt: {}", HEXUPPER.encode(&salt));
+    println!("PBKDF2 hash: {}", HEXUPPER.encode(&pbkdf2_hash));
+
+    // `verify` 检查哈希是否正确
+    let should_`succeed = pbkdf2::verify(
+        pbkdf2::PBKDF2_HMAC_SHA512,
+        n_iter,
+        &salt,
+        password.as_bytes(),
+        &pbkdf2_hash,
+    );
+    let wrong_password = "Definitely not the correct password";
+    let should_fail = pbkdf2::verify(
+        pbkdf2::PBKDF2_HMAC_SHA512,
+        n_iter,
+        &salt,
+        wrong_password.as_bytes(),
+        &pbkdf2_hash,
+    );
+
+    assert!(should_succeed.is_ok());
+    assert!(!should_fail.is_ok());
+
+    Ok(())
+}
+```
\ No newline at end of file

From c6b6faa6a6b98947dbdf94253c885c9a99998635 Mon Sep 17 00:00:00 2001
From: sunface <cto@188.com>
Date: Mon, 4 Apr 2022 16:11:30 +0800
Subject: [PATCH 13/34] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AB=A0=E8=8A=82=20[C?=
 =?UTF-8?q?ookbook=20-=20=E4=BD=8D=E5=AD=97=E6=AE=B5]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/cookbook/cryptography/encryption.md |  2 +-
 src/cookbook/datastructures/bitfield.md | 46 +++++++++++++++++++++++++
 内容变更记录.md                   |  2 ++
 3 files changed, 49 insertions(+), 1 deletion(-)

diff --git a/src/cookbook/cryptography/encryption.md b/src/cookbook/cryptography/encryption.md
index a728da46..becab266 100644
--- a/src/cookbook/cryptography/encryption.md
+++ b/src/cookbook/cryptography/encryption.md
@@ -1,7 +1,7 @@
 # 加密
 
 ### 使用  PBKDF2 对密码进行哈希和加盐( salt )
-[ring::pbkdf2]() 可以对一个加盐密码进行哈希。
+[ring::pbkdf2](https://briansmith.org/rustdoc/ring/pbkdf2/index.html) 可以对一个加盐密码进行哈希。
 
 ```rust,editable
 
diff --git a/src/cookbook/datastructures/bitfield.md b/src/cookbook/datastructures/bitfield.md
index 7f16f82f..4c9c29e0 100644
--- a/src/cookbook/datastructures/bitfield.md
+++ b/src/cookbook/datastructures/bitfield.md
@@ -1 +1,47 @@
 # 位字段
+
+### 定义和操作位字段
+使用 [`bitflags!`](https://docs.rs/bitflags/1.3.2/bitflags/macro.bitflags.html) 宏可以帮助我们创建安全的位字段类型 `MyFlags`,然后为其实现基本的 `clear` 操作。以下代码展示了基本的位操作和格式化:
+```rust,editable
+use bitflags::bitflags;
+use std::fmt;
+
+bitflags! {
+    struct MyFlags: u32 {
+        const FLAG_A       = 0b00000001;
+        const FLAG_B       = 0b00000010;
+        const FLAG_C       = 0b00000100;
+        const FLAG_ABC     = Self::FLAG_A.bits
+                           | Self::FLAG_B.bits
+                           | Self::FLAG_C.bits;
+    }
+}
+
+impl MyFlags {
+    pub fn clear(&mut self) -> &mut MyFlags {
+        self.bits = 0;  
+        self
+    }
+}
+
+impl fmt::Display for MyFlags {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{:032b}", self.bits)
+    }
+}
+
+fn main() {
+    let e1 = MyFlags::FLAG_A | MyFlags::FLAG_C;
+    let e2 = MyFlags::FLAG_B | MyFlags::FLAG_C;
+    assert_eq!((e1 | e2), MyFlags::FLAG_ABC);   
+    assert_eq!((e1 & e2), MyFlags::FLAG_C);    
+    assert_eq!((e1 - e2), MyFlags::FLAG_A);    
+    assert_eq!(!e2, MyFlags::FLAG_A);           
+
+    let mut flags = MyFlags::FLAG_ABC;
+    assert_eq!(format!("{}", flags), "00000000000000000000000000000111");
+    assert_eq!(format!("{}", flags.clear()), "00000000000000000000000000000000");
+    assert_eq!(format!("{:?}", MyFlags::FLAG_B), "FLAG_B");
+    assert_eq!(format!("{:?}", MyFlags::FLAG_A | MyFlags::FLAG_B), "FLAG_A | FLAG_B");
+}
+```
\ No newline at end of file
diff --git a/内容变更记录.md b/内容变更记录.md
index 4b301d2c..af513259 100644
--- a/内容变更记录.md
+++ b/内容变更记录.md
@@ -4,6 +4,8 @@
 ## 2022-04-04 
 
 - 新增章节: [Cookbook - 使用 rayon 并行处理数据](https://course.rs/cookbook/cocurrency/parallel.html)
+- 新增章节: [Cookbook - 加密](https://course.rs/cookbook/cryptography/encryption.html)
+- 新增章节: [Cookbook - 哈希](https://course.rs/cookbook/cryptography/hashing.html)
 
 
 ## 2022-04-03

From f4cf63a55ed34ee4bbf49a64ec36ba55076231a7 Mon Sep 17 00:00:00 2001
From: sunface <cto@188.com>
Date: Mon, 4 Apr 2022 16:12:30 +0800
Subject: [PATCH 14/34] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AB=A0=E8=8A=82=20[C?=
 =?UTF-8?q?ookbook=20-=20=E4=BD=8D=E5=AD=97=E6=AE=B5]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 内容变更记录.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/内容变更记录.md b/内容变更记录.md
index af513259..366527f9 100644
--- a/内容变更记录.md
+++ b/内容变更记录.md
@@ -6,7 +6,7 @@
 - 新增章节: [Cookbook - 使用 rayon 并行处理数据](https://course.rs/cookbook/cocurrency/parallel.html)
 - 新增章节: [Cookbook - 加密](https://course.rs/cookbook/cryptography/encryption.html)
 - 新增章节: [Cookbook - 哈希](https://course.rs/cookbook/cryptography/hashing.html)
-
+- 新增章节: [Cookbook - 位字段](https://course.rs/cookbook/datastructures/bitfield.html)
 
 ## 2022-04-03
 

From 2d43a8e4d414b1792fdf663db56998e4bdeb87f9 Mon Sep 17 00:00:00 2001
From: sunface <cto@188.com>
Date: Mon, 4 Apr 2022 16:17:28 +0800
Subject: [PATCH 15/34] update toc

---
 src/SUMMARY.md | 48 ++++++++++++++++++++++++++----------------------
 1 file changed, 26 insertions(+), 22 deletions(-)

diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index 166218dd..40047d2c 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -162,9 +162,29 @@
     - [构建脚本 build.rs](cargo/reference/build-script/intro.md)
       - [构建脚本示例](cargo/reference/build-script/examples.md)  
 
-
-# 高级专题
+# 应用实战
 ---
+- [CookBook doing](cookbook/intro.md)
+  - [实用算法](cookbook/algos/intro.md)
+    - [生成随机值](cookbook/algos/randomness.md)
+    - [Vec 排序](cookbook/algos/sorting.md)
+  - [命令行]()
+    - [参数解析](cookbook/cmd/parsing.md)
+    - [终端输出格式化](cookbook/cmd/ansi.md)
+  - [压缩]()
+    - [使用.tar包](cookbook/compression/tar.md)
+  - [并发]()
+    - [线程](cookbook/cocurrency/threads.md)
+    - [使用rayon并行处理数据](cookbook/cocurrency/parallel.md)
+  - [密码学]()
+    - [哈希](cookbook/cryptography/hashing.md)
+    - [加密](cookbook/cryptography/encryption.md)
+  - [数据结构]()
+    - [位字段](cookbook/datastructures/bitfield.md)
+  - [数据库]()
+    - [SQLite](cookbook/database/sqlite.md)
+    - [Postgres](cookbook/database/postgres.md)
+
 - [Rust 最佳实践](practice/intro.md)
   - [对抗编译检查](practice/fight-with-compiler/intro.md)
     - [生命周期](practice/fight-with-compiler/lifetime/intro.md)
@@ -231,6 +251,9 @@
       - [栈上的链表](too-many-lists/advanced-lists/stack-allocated.md)
 
 
+# 高级专题
+---
+
 - [Rust 性能优化 todo](profiling/intro.md)
   - [深入内存 todo](profiling/memory/intro.md)
     - [指针和引用 todo](profiling/memory/pointer-ref.md)
@@ -263,26 +286,7 @@
   - [HashMap todo](std/hashmap.md)
   - [Iterator 常用方法 todo](std/iterator.md)
 
-- [CookBook doing](cookbook/intro.md)
-  - [实用算法](cookbook/algos/intro.md)
-    - [生成随机值](cookbook/algos/randomness.md)
-    - [Vec 排序](cookbook/algos/sorting.md)
-  - [命令行]()
-    - [参数解析](cookbook/cmd/parsing.md)
-    - [终端输出格式化](cookbook/cmd/ansi.md)
-  - [压缩]()
-    - [使用.tar包](cookbook/compression/tar.md)
-  - [并发]()
-    - [线程](cookbook/cocurrency/threads.md)
-    - [使用rayon并行处理数据](cookbook/cocurrency/parallel.md)
-  - [密码学]()
-    - [哈希](cookbook/cryptography/hashing.md)
-    - [加密](cookbook/cryptography/encryption.md)
-  - [数据结构]()
-    - [位字段](cookbook/datastructures/bitfield.md)
-  - [数据库]()
-    - [SQLite](cookbook/database/sqlite.md)
-    - [Postgres](cookbook/database/postgres.md)
+
 
   <!-- - [配置文件解析 todo](cookbook/config.md)
   - [编解码 todo](cookbook/encoding/intro.md)

From 22f95622c3aaf813b34d4721ddf52aba1be317f7 Mon Sep 17 00:00:00 2001
From: Allan Downey <AllanDowney@126.com>
Date: Mon, 4 Apr 2022 17:56:00 +0800
Subject: [PATCH 16/34] update(index-list): add match list and match guard

---
 src/index-list.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/index-list.md b/src/index-list.md
index 5bc36313..f2f4047f 100644
--- a/src/index-list.md
+++ b/src/index-list.md
@@ -200,8 +200,10 @@
 | 名称          | 关键字     | 简介                                                                                                                                                               |
 | ------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
 | [模式绑定]    | 模式匹配   | 从模式中取出绑定的值                                                                                                                                               |
+| [全模式列表]  | 模式匹配   | 列出了所有的模式匹配                                                                                                                                               |
 | [match 匹配]  | 模式匹配   | `match` 的匹配必须要穷举出所有可能,因此这里用 `_ ` 来代表未列出的所有可能性<br>`match` 的每一个分支都必须是一个表达式,且所有分支的表达式最终返回值的类型必须相同 |
 | [matches! 宏] | 模式匹配   | 将一个表达式跟模式进行匹配,然后返回匹配的结果 `true` 或 `false`                                                                                                   |
+| [match guard] | 匹配守卫   | 位于 `match` 分支模式之后的额外 `if` 条件,它能为分支模式提供更进一步的匹配条件                                                                                    |
 | [move 移动]   | 转移所有权 | `let s2 = s1;`<br>`s1` 所有权转移给了 `s2`,`s1` 失效                                                                                                              |
 | M             | KWM        | MIntroduction                                                                                                                                                      |
 
@@ -209,6 +211,8 @@
 [match 匹配]: https://course.rs/basic/match-pattern/match-if-let.html#match-匹配
 [matches! 宏]: https://course.rs/basic/match-pattern/match-if-let.html#matches宏
 [move 移动]: https://course.rs/basic/ownership/ownership.html#转移所有权
+[全模式列表]: https://course.rs/basic/match-pattern/all-patterns.html
+[match guard]: https://course.rs/basic/match-pattern/all-patterns.html#匹配守卫提供的额外条件
 
 [back](#head)
 

From 52d0c47375bffe2320c4a4c7eb1f0eec3204308f Mon Sep 17 00:00:00 2001
From: sunface <cto@188.com>
Date: Mon, 4 Apr 2022 20:23:23 +0800
Subject: [PATCH 17/34] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AB=A0=20[Cookbook?=
 =?UTF-8?q?=20-=20=E6=95=B0=E6=8D=AE=E5=BA=93]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/cookbook/database/postgres.md | 122 +++++++++++++++++++++++++++
 src/cookbook/database/sqlite.md   | 135 ++++++++++++++++++++++++++++++
 2 files changed, 257 insertions(+)

diff --git a/src/cookbook/database/postgres.md b/src/cookbook/database/postgres.md
index a7be4c31..13bd37dd 100644
--- a/src/cookbook/database/postgres.md
+++ b/src/cookbook/database/postgres.md
@@ -1 +1,123 @@
 # Postgres
+
+### 在数据库中创建表格
+我们通过 [postgres](https://docs.rs/postgres/0.17.2/postgres/) 来操作数据库。下面的例子有一个前提:数据库 `library` 已经存在,其中用户名和密码都是 `postgres`。
+
+```rust,editable
+use postgres::{Client, NoTls, Error};
+
+fn main() -> Result<(), Error> {
+    // 连接到数据库 library
+    let mut client = Client::connect("postgresql://postgres:postgres@localhost/library", NoTls)?;
+    
+    client.batch_execute("
+        CREATE TABLE IF NOT EXISTS author (
+            id              SERIAL PRIMARY KEY,
+            name            VARCHAR NOT NULL,
+            country         VARCHAR NOT NULL
+            )
+    ")?;
+
+    client.batch_execute("
+        CREATE TABLE IF NOT EXISTS book  (
+            id              SERIAL PRIMARY KEY,
+            title           VARCHAR NOT NULL,
+            author_id       INTEGER NOT NULL REFERENCES author
+            )
+    ")?;
+
+    Ok(())
+
+}
+```
+
+### 插入和查询
+
+```rust,editable
+use postgres::{Client, NoTls, Error};
+use std::collections::HashMap;
+
+struct Author {
+    _id: i32,
+    name: String,
+    country: String
+}
+
+fn main() -> Result<(), Error> {
+    let mut client = Client::connect("postgresql://postgres:postgres@localhost/library", 
+                                    NoTls)?;
+    
+    let mut authors = HashMap::new();
+    authors.insert(String::from("Chinua Achebe"), "Nigeria");
+    authors.insert(String::from("Rabindranath Tagore"), "India");
+    authors.insert(String::from("Anita Nair"), "India");
+
+    for (key, value) in &authors {
+        let author = Author {
+            _id: 0,
+            name: key.to_string(),
+            country: value.to_string()
+        };
+
+        // 插入数据
+        client.execute(
+                "INSERT INTO author (name, country) VALUES ($1, $2)",
+                &[&author.name, &author.country],
+        )?;
+    }
+
+    // 查询数据
+    for row in client.query("SELECT id, name, country FROM author", &[])? {
+        let author = Author {
+            _id: row.get(0),
+            name: row.get(1),
+            country: row.get(2),
+        };
+        println!("Author {} is from {}", author.name, author.country);
+    }
+
+    Ok(())
+
+}
+```
+
+### 聚合数据
+
+下面代码将使用降序的方式列出 [Museum of Modern Art]() 数据库中的前 7999 名艺术家的国籍分布.
+
+```rust,editable
+use postgres::{Client, Error, NoTls};
+
+struct Nation {
+    nationality: String,
+    count: i64,
+}
+
+fn main() -> Result<(), Error> {
+    let mut client = Client::connect(
+        "postgresql://postgres:postgres@127.0.0.1/moma",
+        NoTls,
+    )?;
+
+    for row in client.query 
+    ("SELECT nationality, COUNT(nationality) AS count 
+    FROM artists GROUP BY nationality ORDER BY count DESC", &[])? {
+        
+        let (nationality, count) : (Option<String>, Option<i64>) 
+        = (row.get (0), row.get (1));
+        
+        if nationality.is_some () && count.is_some () {
+
+            let nation = Nation{
+                nationality: nationality.unwrap(),
+                count: count.unwrap(),
+        };
+            println!("{} {}", nation.nationality, nation.count);
+            
+        }
+    }
+
+    Ok(())
+}
+```
+
diff --git a/src/cookbook/database/sqlite.md b/src/cookbook/database/sqlite.md
index 02122687..0872fbf2 100644
--- a/src/cookbook/database/sqlite.md
+++ b/src/cookbook/database/sqlite.md
@@ -1 +1,136 @@
 # SQLite
+
+### 创建 SQLite 数据库
+
+使用 `rusqlite` 可以创建 SQLite 数据库,[Connection::open](https://docs.rs/rusqlite/*/rusqlite/struct.Connection.html#method.open) 会尝试打开一个数据库,若不存在,则创建新的数据库。
+
+> 这里创建的 `cats.db` 数据库将被后面的例子所使用
+
+
+```rust,editable
+use rusqlite::{Connection, Result};
+use rusqlite::NO_PARAMS;
+
+fn main() -> Result<()> {
+    let conn = Connection::open("cats.db")?;
+
+    conn.execute(
+        "create table if not exists cat_colors (
+             id integer primary key,
+             name text not null unique
+         )",
+        NO_PARAMS,
+    )?;
+    conn.execute(
+        "create table if not exists cats (
+             id integer primary key,
+             name text not null,
+             color_id integer not null references cat_colors(id)
+         )",
+        NO_PARAMS,
+    )?;
+
+    Ok(())
+}
+```
+
+### 插入和查询
+
+```rust,editable
+
+use rusqlite::NO_PARAMS;
+use rusqlite::{Connection, Result};
+use std::collections::HashMap;
+
+#[derive(Debug)]
+struct Cat {
+    name: String,
+    color: String,
+}
+
+fn main() -> Result<()> {
+    // 打开第一个例子所创建的数据库
+    let conn = Connection::open("cats.db")?;
+
+    let mut cat_colors = HashMap::new();
+    cat_colors.insert(String::from("Blue"), vec!["Tigger", "Sammy"]);
+    cat_colors.insert(String::from("Black"), vec!["Oreo", "Biscuit"]);
+
+    for (color, catnames) in &cat_colors {
+        // 插入一条数据行
+        conn.execute(
+            "INSERT INTO cat_colors (name) values (?1)",
+            &[&color.to_string()],
+        )?;
+        // 获取最近插入数据行的 id
+        let last_id: String = conn.last_insert_rowid().to_string();
+
+        for cat in catnames {
+            conn.execute(
+                "INSERT INTO cats (name, color_id) values (?1, ?2)",
+                &[&cat.to_string(), &last_id],
+            )?;
+        }
+    }
+    let mut stmt = conn.prepare(
+        "SELECT c.name, cc.name from cats c
+         INNER JOIN cat_colors cc
+         ON cc.id = c.color_id;",
+    )?;
+
+    let cats = stmt.query_map(NO_PARAMS, |row| {
+        Ok(Cat {
+            name: row.get(0)?,
+            color: row.get(1)?,
+        })
+    })?;
+
+    for cat in cats {
+        println!("Found cat {:?}", cat);
+    }
+
+    Ok(())
+}
+```
+
+### 使用事务
+使用 [Connection::transaction](https://docs.rs/rusqlite/*/rusqlite/struct.Connection.html#method.transaction) 可以开始新的事务,若没有对事务进行显式地提交 [Transaction::commit](https://docs.rs/rusqlite/0.27.0/rusqlite/struct.Transaction.html#method.commit),则会进行回滚。
+
+下面的例子中,`rolled_back_tx` 插入了重复的颜色名称,会发生回滚。
+
+```rust,editable
+use rusqlite::{Connection, Result, NO_PARAMS};
+
+fn main() -> Result<()> {
+    // 打开第一个例子所创建的数据库
+    let mut conn = Connection::open("cats.db")?;
+
+    successful_tx(&mut conn)?;
+
+    let res = rolled_back_tx(&mut conn);
+    assert!(res.is_err());
+
+    Ok(())
+}
+
+fn successful_tx(conn: &mut Connection) -> Result<()> {
+    let tx = conn.transaction()?;
+
+    tx.execute("delete from cat_colors", NO_PARAMS)?;
+    tx.execute("insert into cat_colors (name) values (?1)", &[&"lavender"])?;
+    tx.execute("insert into cat_colors (name) values (?1)", &[&"blue"])?;
+
+    tx.commit()
+}
+
+fn rolled_back_tx(conn: &mut Connection) -> Result<()> {
+    let tx = conn.transaction()?;
+
+    tx.execute("delete from cat_colors", NO_PARAMS)?;
+    tx.execute("insert into cat_colors (name) values (?1)", &[&"lavender"])?;
+    tx.execute("insert into cat_colors (name) values (?1)", &[&"blue"])?;
+    tx.execute("insert into cat_colors (name) values (?1)", &[&"lavender"])?;
+
+    tx.commit()
+}
+```
\ No newline at end of file

From 8f44ee5ea03a70fb480b648ed63a390683416fa4 Mon Sep 17 00:00:00 2001
From: sunface <cto@188.com>
Date: Mon, 4 Apr 2022 20:24:10 +0800
Subject: [PATCH 18/34] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AB=A0=20[Cookbook?=
 =?UTF-8?q?=20-=20=E6=95=B0=E6=8D=AE=E5=BA=93]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 内容变更记录.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/内容变更记录.md b/内容变更记录.md
index 366527f9..9b9b38ca 100644
--- a/内容变更记录.md
+++ b/内容变更记录.md
@@ -7,6 +7,8 @@
 - 新增章节: [Cookbook - 加密](https://course.rs/cookbook/cryptography/encryption.html)
 - 新增章节: [Cookbook - 哈希](https://course.rs/cookbook/cryptography/hashing.html)
 - 新增章节: [Cookbook - 位字段](https://course.rs/cookbook/datastructures/bitfield.html)
+- 新增章节: [Cookbook - 数据库](https://course.rs/cookbook/database/sqlite.html)
+
 
 ## 2022-04-03
 

From cc7281e0ffb725cbf5e863262f7240cbdd4960bc Mon Sep 17 00:00:00 2001
From: sunface <cto@188.com>
Date: Mon, 4 Apr 2022 20:44:04 +0800
Subject: [PATCH 19/34] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AB=A0=20[Cookbook?=
 =?UTF-8?q?=20-=20=E6=97=B6=E9=97=B4=E8=AE=A1=E7=AE=97=E5=92=8C=E8=BD=AC?=
 =?UTF-8?q?=E6=8D=A2]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/SUMMARY.md                       |  9 ++++
 src/cookbook/datetime/duration.md    | 69 ++++++++++++++++++++++++++++
 src/cookbook/datetime/parsing.md     |  1 +
 src/cookbook/devtools/build-tools.md |  1 +
 src/cookbook/devtools/config-log.md  |  1 +
 src/cookbook/devtools/log.md         |  1 +
 src/cookbook/devtools/version.md     |  1 +
 7 files changed, 83 insertions(+)
 create mode 100644 src/cookbook/datetime/duration.md
 create mode 100644 src/cookbook/datetime/parsing.md
 create mode 100644 src/cookbook/devtools/build-tools.md
 create mode 100644 src/cookbook/devtools/config-log.md
 create mode 100644 src/cookbook/devtools/log.md
 create mode 100644 src/cookbook/devtools/version.md

diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index 40047d2c..ab32cbe2 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -184,6 +184,15 @@
   - [数据库]()
     - [SQLite](cookbook/database/sqlite.md)
     - [Postgres](cookbook/database/postgres.md)
+  - [日期和时间]()
+    - [时间计算和转换](cookbook/datetime/duration.md)
+    - [解析和显示](cookbook/datetime/parsing.md)
+  - [开发者工具]()
+    - [日志](cookbook/devtools/log.md)
+    - [配置日志](cookbook/devtools/config-log.md)
+    - [版本](cookbook/devtools/version.md)
+    - [构建时工具](cookbook/devtools/build-tools.md)
+
 
 - [Rust 最佳实践](practice/intro.md)
   - [对抗编译检查](practice/fight-with-compiler/intro.md)
diff --git a/src/cookbook/datetime/duration.md b/src/cookbook/datetime/duration.md
new file mode 100644
index 00000000..4f14bff9
--- /dev/null
+++ b/src/cookbook/datetime/duration.md
@@ -0,0 +1,69 @@
+# 时间计算和转换
+
+### 测量某段代码的耗时
+测量从 [time::Instant::now](https://doc.rust-lang.org/std/time/struct.Instant.html#method.now) 开始所经过的时间 [time::Instant::elapsed](https://doc.rust-lang.org/std/time/struct.Instant.html#method.elapsed).
+
+```rust,editable
+use std::time::{Duration, Instant};
+
+fn main() {
+    let start = Instant::now();
+    expensive_function();
+    let duration = start.elapsed();
+
+    println!("Time elapsed in expensive_function() is: {:?}", duration);
+}
+```
+
+### 对日期和时间进行计算
+使用 [DateTime::checked_add_signed](https://docs.rs/chrono/*/chrono/struct.Date.html#method.checked_add_signed) 计算和显示从现在开始两周后的日期和时间,然后再计算一天前的日期 [DateTime::checked_sub_signed](https://docs.rs/chrono/*/chrono/struct.Date.html#method.checked_sub_signed)。
+
+[DateTime::format](https://docs.rs/chrono/*/chrono/struct.DateTime.html#method.format) 所支持的转义序列可以在 [chrono::format::strftime](https://docs.rs/chrono/*/chrono/format/strftime/index.html) 找到.
+
+```rust,editable
+use chrono::{DateTime, Duration, Utc};
+
+fn day_earlier(date_time: DateTime<Utc>) -> Option<DateTime<Utc>> {
+    date_time.checked_sub_signed(Duration::days(1))
+}
+
+fn main() {
+    let now = Utc::now();
+    println!("{}", now);
+
+    let almost_three_weeks_from_now = now.checked_add_signed(Duration::weeks(2))
+            .and_then(|in_2weeks| in_2weeks.checked_add_signed(Duration::weeks(1)))
+            .and_then(day_earlier);
+
+    match almost_three_weeks_from_now {
+        Some(x) => println!("{}", x),
+        None => eprintln!("Almost three weeks from now overflows!"),
+    }
+
+    match now.checked_add_signed(Duration::max_value()) {
+        Some(x) => println!("{}", x),
+        None => eprintln!("We can't use chrono to tell the time for the Solar System to complete more than one full orbit around the galactic center."),
+    }
+}
+```
+
+### 将本地时间转换成其它时区
+使用 [offset::Local::now](https://docs.rs/chrono/*/chrono/offset/struct.Local.html#method.now) 获取本地时间并进行显示,接着,使用 [DateTime::from_utc](https://docs.rs/chrono/*/chrono/struct.DateTime.html#method.from_utc) 将它转换成 UTC 标准时间。最后,再使用 [offset::FixedOffset](https://docs.rs/chrono/*/chrono/offset/struct.FixedOffset.html) 将 UTC 时间转换成 UTC+8 和 UTC-2 的时间。
+
+```rust,editable
+use chrono::{DateTime, FixedOffset, Local, Utc};
+
+fn main() {
+    let local_time = Local::now();
+    let utc_time = DateTime::<Utc>::from_utc(local_time.naive_utc(), Utc);
+    let china_timezone = FixedOffset::east(8 * 3600);
+    let rio_timezone = FixedOffset::west(2 * 3600);
+    println!("Local time now is {}", local_time);
+    println!("UTC time now is {}", utc_time);
+    println!(
+        "Time in Hong Kong now is {}",
+        utc_time.with_timezone(&china_timezone)
+    );
+    println!("Time in Rio de Janeiro now is {}", utc_time.with_timezone(&rio_timezone));
+}
+```
\ No newline at end of file
diff --git a/src/cookbook/datetime/parsing.md b/src/cookbook/datetime/parsing.md
new file mode 100644
index 00000000..7a3afa08
--- /dev/null
+++ b/src/cookbook/datetime/parsing.md
@@ -0,0 +1 @@
+# 解析和显示
diff --git a/src/cookbook/devtools/build-tools.md b/src/cookbook/devtools/build-tools.md
new file mode 100644
index 00000000..d5694492
--- /dev/null
+++ b/src/cookbook/devtools/build-tools.md
@@ -0,0 +1 @@
+# 构建时工具
diff --git a/src/cookbook/devtools/config-log.md b/src/cookbook/devtools/config-log.md
new file mode 100644
index 00000000..e0099ddb
--- /dev/null
+++ b/src/cookbook/devtools/config-log.md
@@ -0,0 +1 @@
+# 配置日志
diff --git a/src/cookbook/devtools/log.md b/src/cookbook/devtools/log.md
new file mode 100644
index 00000000..65c2dbdd
--- /dev/null
+++ b/src/cookbook/devtools/log.md
@@ -0,0 +1 @@
+# 日志
diff --git a/src/cookbook/devtools/version.md b/src/cookbook/devtools/version.md
new file mode 100644
index 00000000..51dd098e
--- /dev/null
+++ b/src/cookbook/devtools/version.md
@@ -0,0 +1 @@
+# 版本

From 8e6aed737fec6c41e5da33b3f43f3ce2e12fbafb Mon Sep 17 00:00:00 2001
From: sunface <cto@188.com>
Date: Mon, 4 Apr 2022 21:13:23 +0800
Subject: [PATCH 20/34] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AB=A0=20[Cookbook?=
 =?UTF-8?q?=20-=20=E6=97=B6=E9=97=B4=E8=A7=A3=E6=9E=90=E5=92=8C=E6=98=BE?=
 =?UTF-8?q?=E7=A4=BA]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/cookbook/datetime/parsing.md | 113 +++++++++++++++++++++++++++++++
 1 file changed, 113 insertions(+)

diff --git a/src/cookbook/datetime/parsing.md b/src/cookbook/datetime/parsing.md
index 7a3afa08..aee55576 100644
--- a/src/cookbook/datetime/parsing.md
+++ b/src/cookbook/datetime/parsing.md
@@ -1 +1,114 @@
 # 解析和显示
+
+### 检查日期和时间
+通过 [DateTime](https://docs.rs/chrono/*/chrono/struct.DateTime.html) 获取当前的 UTC 时间:
+- [Timelike](https://docs.rs/chrono/*/chrono/trait.Timelike.html), 时/分/秒
+- [Datelike](https://docs.rs/chrono/*/chrono/trait.Datelike.html), 年/月/日
+
+```rust,editable
+use chrono::{Datelike, Timelike, Utc};
+
+fn main() {
+    let now = Utc::now();
+
+    let (is_pm, hour) = now.hour12();
+    println!(
+        "The current UTC time is {:02}:{:02}:{:02} {}",
+        hour,
+        now.minute(),
+        now.second(),
+        if is_pm { "PM" } else { "AM" }
+    );
+    println!(
+        "And there have been {} seconds since midnight",
+        now.num_seconds_from_midnight()
+    );
+
+    let (is_common_era, year) = now.year_ce();
+    println!(
+        "The current UTC date is {}-{:02}-{:02} {:?} ({})",
+        year,
+        now.month(),
+        now.day(),
+        now.weekday(),
+        if is_common_era { "CE" } else { "BCE" }
+    );
+    println!(
+        "And the Common Era began {} days ago",
+        now.num_days_from_ce()
+    );
+}
+```
+
+### 日期和时间戳的相互转换
+
+```rust,editable
+use chrono::{NaiveDate, NaiveDateTime};
+
+fn main() {
+    // 生成一个具体的日期时间
+    let date_time: NaiveDateTime = NaiveDate::from_ymd(2017, 11, 12).and_hms(17, 33, 44);
+    println!(
+        "Number of seconds between 1970-01-01 00:00:00 and {} is {}.",
+        // 打印日期和日期对应的时间戳
+        date_time, date_time.timestamp());
+
+    // 计算从 1970 1月1日 0:00:00 UTC 开始,10亿秒后是什么日期时间
+    let date_time_after_a_billion_seconds = NaiveDateTime::from_timestamp(1_000_000_000, 0);
+    println!(
+        "Date after a billion seconds since 1970-01-01 00:00:00 was {}.",
+        date_time_after_a_billion_seconds);
+}
+```
+
+### 显示格式化的日期和时间
+通过 [Utc::now](https://docs.rs/chrono/*/chrono/offset/struct.Utc.html#method.now) 可以获取当前的 UTC 时间。
+
+```rust,editable
+use chrono::{DateTime, Utc};
+
+fn main() {
+    let now: DateTime<Utc> = Utc::now();
+
+    println!("UTC now is: {}", now);
+    // 使用 RFC 2822 格式显示当前时间
+    println!("UTC now in RFC 2822 is: {}", now.to_rfc2822());
+    // 使用 RFC 3339 格式显示当前时间
+    println!("UTC now in RFC 3339 is: {}", now.to_rfc3339());
+    // 使用自定义格式显示当前时间
+    println!("UTC now in a custom format is: {}", now.format("%a %b %e %T %Y"));
+}
+```
+
+### 将字符串解析为 DateTime 结构体
+我们可以将多种格式的日期时间字符串转换成 [DateTime](https://docs.rs/chrono/*/chrono/struct.DateTime.html) 结构体。[DateTime::parse_from_str](https://docs.rs/chrono/*/chrono/struct.DateTime.html#method.parse_from_str) 使用的转义序列可以在 [chrono::format::strftime](https://docs.rs/chrono/0.4.19/chrono/format/strftime/index.html) 找到.
+
+只有当能唯一的标识出日期和时间时,才能创建 `DateTime`。如果要在没有时区的情况下解析日期或时间,你需要使用 [`NativeDate`](https://docs.rs/chrono/*/chrono/naive/struct.NaiveDate.html) 等函数。
+
+```rust,editable
+use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime};
+use chrono::format::ParseError;
+
+
+fn main() -> Result<(), ParseError> {
+    let rfc2822 = DateTime::parse_from_rfc2822("Tue, 1 Jul 2003 10:52:37 +0200")?;
+    println!("{}", rfc2822);
+
+    let rfc3339 = DateTime::parse_from_rfc3339("1996-12-19T16:39:57-08:00")?;
+    println!("{}", rfc3339);
+    
+    let custom = DateTime::parse_from_str("5.8.1994 8:00 am +0000", "%d.%m.%Y %H:%M %P %z")?;
+    println!("{}", custom);
+
+    let time_only = NaiveTime::parse_from_str("23:56:04", "%H:%M:%S")?;
+    println!("{}", time_only);
+
+    let date_only = NaiveDate::parse_from_str("2015-09-05", "%Y-%m-%d")?;
+    println!("{}", date_only);
+
+    let no_timezone = NaiveDateTime::parse_from_str("2015-09-05 23:56:04", "%Y-%m-%d %H:%M:%S")?;
+    println!("{}", no_timezone);
+
+    Ok(())
+}
+```
\ No newline at end of file

From 933c7164fe03b1b5fd2196eaac1345128df0381a Mon Sep 17 00:00:00 2001
From: sunface <cto@188.com>
Date: Mon, 4 Apr 2022 21:52:12 +0800
Subject: [PATCH 21/34] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AB=A0=20[Cookbook?=
 =?UTF-8?q?=20-=20=E6=97=A5=E5=BF=97]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/cookbook/devtools/log.md | 126 +++++++++++++++++++++++++++++++++++
 1 file changed, 126 insertions(+)

diff --git a/src/cookbook/devtools/log.md b/src/cookbook/devtools/log.md
index 65c2dbdd..adc6616a 100644
--- a/src/cookbook/devtools/log.md
+++ b/src/cookbook/devtools/log.md
@@ -1 +1,127 @@
 # 日志
+
+## log 包
+[log](https://docs.rs/crate/log/0.4.16) 提供了日志相关的实用工具。
+
+### 在控制台打印 debug 信息
+`env_logger` 通过环境变量来配置日志。[log::debug!](https://docs.rs/log/0.4.16/log/macro.debug.html) 使用起来跟 [std::fmt](https://doc.rust-lang.org/std/fmt/) 中的格式化字符串很像。
+
+```rust
+fn execute_query(query: &str) {
+    log::debug!("Executing query: {}", query);
+}
+
+fn main() {
+    env_logger::init();
+
+    execute_query("DROP TABLE students");
+}
+```
+
+如果大家运行代码,会发现没有任何日志输出,原因是默认的日志级别是 `error`,因此我们需要通过 `RUST_LOG` 环境变量来设置下新的日志级别:
+```shell
+$ RUST_LOG=debug cargo run
+```
+
+然后你将成功看到以下输出:
+```shell
+DEBUG:main: Executing query: DROP TABLE students
+```
+
+### 将错误日志输出到控制台
+下面我们通过 [log::error!](https://docs.rs/log/0.4.16/log/macro.error.html) 将错误日志输出到标准错误 `stderr`。
+
+```rust
+fn execute_query(_query: &str) -> Result<(), &'static str> {
+    Err("I'm afraid I can't do that")
+}
+
+fn main() {
+    env_logger::init();
+
+    let response = execute_query("DROP TABLE students");
+    if let Err(err) = response {
+        log::error!("Failed to execute query: {}", err);
+    }
+}
+```
+
+### 将错误输出到标准输出 stdout
+默认的错误会输出到标准错误输出 `stderr`,下面我们通过自定的配置来让错误输出到标准输出 `stdout`。
+
+```rust,editable
+use env_logger::{Builder, Target};
+
+fn main() {
+    Builder::new()
+        .target(Target::Stdout)
+        .init();
+
+    log::error!("This error has been printed to Stdout");
+}
+```
+
+### 使用自定义 logger
+下面的代码将实现一个自定义 logger `ConsoleLogger`,输出到标准输出 `stdout`。为了使用日志宏,`ConsoleLogger` 需要实现 [log::Log](https://docs.rs/log/*/log/trait.Log.html) 特征,然后使用 [log::set_logger](https://docs.rs/log/*/log/fn.set_logger.html) 来安装使用。
+
+```rust,editable
+use log::{Record, Level, Metadata, LevelFilter, SetLoggerError};
+
+static CONSOLE_LOGGER: ConsoleLogger = ConsoleLogger;
+
+struct ConsoleLogger;
+
+impl log::Log for ConsoleLogger {
+  fn enabled(&self, metadata: &Metadata) -> bool {
+     metadata.level() <= Level::Info
+    }
+
+    fn log(&self, record: &Record) {
+        if self.enabled(record.metadata()) {
+            println!("Rust says: {} - {}", record.level(), record.args());
+        }
+    }
+
+    fn flush(&self) {}
+}
+
+fn main() -> Result<(), SetLoggerError> {
+    log::set_logger(&CONSOLE_LOGGER)?;
+    log::set_max_level(LevelFilter::Info);
+
+    log::info!("hello log");
+    log::warn!("warning");
+    log::error!("oops");
+    Ok(())
+}
+```
+
+### 输出到 Unix syslog
+下面的代码将使用 [syslog](https://docs.rs/crate/syslog/6.0.1) 包将日志输出到 [Unix Syslog](https://www.gnu.org/software/libc/manual/html_node/Overview-of-Syslog.html).
+
+```rust,editable
+#[cfg(target_os = "linux")]
+#[cfg(target_os = "linux")]
+use syslog::{Facility, Error};
+
+#[cfg(target_os = "linux")]
+fn main() -> Result<(), Error> {
+    // 初始化 logger
+    syslog::init(Facility::LOG_USER,
+                 log::LevelFilter::Debug,
+                 // 可选的应用名称
+                 Some("My app name"))?;
+    log::debug!("this is a debug {}", "message");
+    log::error!("this is an error!");
+    Ok(())
+}
+
+#[cfg(not(target_os = "linux"))]
+fn main() {
+    println!("So far, only Linux systems are supported.");
+}
+```
+
+
+## tracing
+@todo
\ No newline at end of file

From 71fc75974e9a5745922e32ec1c5a5a781614a04a Mon Sep 17 00:00:00 2001
From: sunface <cto@188.com>
Date: Mon, 4 Apr 2022 22:13:35 +0800
Subject: [PATCH 22/34] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AB=A0=20[Cookbook?=
 =?UTF-8?q?=20-=20=E9=85=8D=E7=BD=AE=E6=97=A5=E5=BF=97]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 book.toml                           |   2 +-
 src/cookbook/devtools/config-log.md | 139 ++++++++++++++++++++++++++++
 2 files changed, 140 insertions(+), 1 deletion(-)

diff --git a/book.toml b/book.toml
index 5e439c80..22f16de7 100644
--- a/book.toml
+++ b/book.toml
@@ -13,7 +13,7 @@ edit-url-template = "https://github.com/sunface/rust-course/edit/main/{path}"
 [output.html.playground]
 editable = true
 copy-js = true
-line-numbers = true
+# line-numbers = true
 
 [output.html.fold]
 enable = true
diff --git a/src/cookbook/devtools/config-log.md b/src/cookbook/devtools/config-log.md
index e0099ddb..1121571f 100644
--- a/src/cookbook/devtools/config-log.md
+++ b/src/cookbook/devtools/config-log.md
@@ -1 +1,140 @@
 # 配置日志
+
+### 为每个模块开启独立的日志级别
+下面代码创建了模块 `foo` 和嵌套模块 `foo::bar`,并通过 [RUST_LOG](https://docs.rs/env_logger/*/env_logger/#enabling-logging) 环境变量对各自的日志级别进行了控制。
+
+```rust,editable
+mod foo {
+    mod bar {
+        pub fn run() {
+            log::warn!("[bar] warn");
+            log::info!("[bar] info");
+            log::debug!("[bar] debug");
+        }
+    }
+
+    pub fn run() {
+        log::warn!("[foo] warn");
+        log::info!("[foo] info");
+        log::debug!("[foo] debug");
+        bar::run();
+    }
+}
+
+fn main() {
+    env_logger::init();
+    log::warn!("[root] warn");
+    log::info!("[root] info");
+    log::debug!("[root] debug");
+    foo::run();
+}
+```
+
+要让环境变量生效,首先需要通过 `env_logger::init()` 开启相关的支持。然后通过以下命令来运行程序:
+```shell
+RUST_LOG="warn,test::foo=info,test::foo::bar=debug" ./test
+```
+
+此时的默认日志级别被设置为 `warn`,但我们还将 `foo` 模块级别设置为 `info`, `foo::bar` 模块日志级别设置为 `debug`。
+
+```bash
+WARN:test: [root] warn
+WARN:test::foo: [foo] warn
+INFO:test::foo: [foo] info
+WARN:test::foo::bar: [bar] warn
+INFO:test::foo::bar: [bar] info
+DEBUG:test::foo::bar: [bar] debug
+```
+
+### 使用自定义环境变量来设置日志
+
+[Builder](https://docs.rs/env_logger/*/env_logger/struct.Builder.html) 将对日志进行配置,以下代码使用 `MY_APP_LOG` 来替代 `RUST_LOG` 环境变量:
+
+```rust,editable
+use std::env;
+use env_logger::Builder;
+
+fn main() {
+    Builder::new()
+        .parse(&env::var("MY_APP_LOG").unwrap_or_default())
+        .init();
+
+    log::info!("informational message");
+    log::warn!("warning message");
+    log::error!("this is an error {}", "message");
+}
+```
+
+### 在日志中包含时间戳
+
+```rust,editable
+use std::io::Write;
+use chrono::Local;
+use env_logger::Builder;
+use log::LevelFilter;
+
+fn main() {
+    Builder::new()
+        .format(|buf, record| {
+            writeln!(buf,
+                "{} [{}] - {}",
+                Local::now().format("%Y-%m-%dT%H:%M:%S"),
+                record.level(),
+                record.args()
+            )
+        })
+        .filter(None, LevelFilter::Info)
+        .init();
+
+    log::warn!("warn");
+    log::info!("info");
+    log::debug!("debug");
+}
+```
+
+以下是 `stderr` 的输出:
+```shell
+2022-03-22T21:57:06 [WARN] - warn
+2022-03-22T21:57:06 [INFO] - info
+```
+
+### 将日志输出到指定文件
+[log4rs](https://docs.rs/log4rs/) 可以帮我们将日志输出指定的位置,它可以使用外部 YAML 文件或 `builder` 的方式进行配置。
+
+```rust,editable
+# use error_chain::error_chain;
+
+use log::LevelFilter;
+use log4rs::append::file::FileAppender;
+use log4rs::encode::pattern::PatternEncoder;
+use log4rs::config::{Appender, Config, Root};
+
+#error_chain! {
+#    foreign_links {
+#        Io(std::io::Error);
+#        LogConfig(log4rs::config::Errors);
+#        SetLogger(log::SetLoggerError);
+#    }
+#}
+
+fn main() -> Result<()> {
+    // 创建日志配置,并指定输出的位置
+    let logfile = FileAppender::builder()
+        // 编码模式的详情参见: https://docs.rs/log4rs/1.0.0/log4rs/encode/pattern/index.html
+        .encoder(Box::new(PatternEncoder::new("{l} - {m}\n")))
+        .build("log/output.log")?;
+
+    let config = Config::builder()
+        .appender(Appender::builder().build("logfile", Box::new(logfile)))
+        .build(Root::builder()
+                   .appender("logfile")
+                   .build(LevelFilter::Info))?;
+
+    log4rs::init_config(config)?;
+
+    log::info!("Hello, world!");
+
+    Ok(())
+}
+
+```
\ No newline at end of file

From f5f102134c7883e0c7aee4456abb31aa261a772a Mon Sep 17 00:00:00 2001
From: sunface <cto@188.com>
Date: Mon, 4 Apr 2022 22:49:44 +0800
Subject: [PATCH 23/34] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AB=A0=20[Cookbook?=
 =?UTF-8?q?=20-=20=E7=89=88=E6=9C=AC=E5=8F=B7]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/cookbook/devtools/version.md | 185 +++++++++++++++++++++++++++++++
 1 file changed, 185 insertions(+)

diff --git a/src/cookbook/devtools/version.md b/src/cookbook/devtools/version.md
index 51dd098e..70ee24e3 100644
--- a/src/cookbook/devtools/version.md
+++ b/src/cookbook/devtools/version.md
@@ -1 +1,186 @@
 # 版本
+
+### 解析并增加版本号
+下面例子使用 [Version::parse](https://docs.rs/semver/*/semver/struct.Version.html#method.parse) 将一个字符串转换成 [semver::Version](https://docs.rs/semver/*/semver/struct.Version.html) 版本号,然后将它的 patch, minor, major 版本号都增加 1。
+
+注意,为了符合[语义化版本的说明](http://semver.org),增加 `minor` 版本时,`patch` 版本会被重设为 `0`,当增加 `major` 版本时,`minor` 和 `patch` 都将被重设为 `0`。
+
+```rust,editable
+use semver::{Version, SemVerError};
+
+fn main() -> Result<(), SemVerError> {
+    let mut parsed_version = Version::parse("0.2.6")?;
+
+    assert_eq!(
+        parsed_version,
+        Version {
+            major: 0,
+            minor: 2,
+            patch: 6,
+            pre: vec![],
+            build: vec![],
+        }
+    );
+
+    parsed_version.increment_patch();
+    assert_eq!(parsed_version.to_string(), "0.2.7");
+    println!("New patch release: v{}", parsed_version);
+
+    parsed_version.increment_minor();
+    assert_eq!(parsed_version.to_string(), "0.3.0");
+    println!("New minor release: v{}", parsed_version);
+
+    parsed_version.increment_major();
+    assert_eq!(parsed_version.to_string(), "1.0.0");
+    println!("New major release: v{}", parsed_version);
+
+    Ok(())
+}
+```
+
+### 解析一个复杂的版本号字符串
+这里的版本号字符串还将包含 `SemVer` 中定义的预发布和构建元信息。
+
+值得注意的是,为了符合 `SemVer` 的规则,构建元信息虽然会被解析,但是在做版本号比较时,该信息会被忽略。换而言之,即使两个版本号的构建字符串不同,它们的版本号依然可能相同。
+
+```rust,editable
+use semver::{Identifier, Version, SemVerError};
+
+fn main() -> Result<(), SemVerError> {
+    let version_str = "1.0.49-125+g72ee7853";
+    let parsed_version = Version::parse(version_str)?;
+
+    assert_eq!(
+        parsed_version,
+        Version {
+            major: 1,
+            minor: 0,
+            patch: 49,
+            pre: vec![Identifier::Numeric(125)],
+            build: vec![],
+        }
+    );
+    assert_eq!(
+        parsed_version.build,
+        vec![Identifier::AlphaNumeric(String::from("g72ee7853"))]
+    );
+
+    let serialized_version = parsed_version.to_string();
+    assert_eq!(&serialized_version, version_str);
+
+    Ok(())
+}
+```
+
+### 检查给定的版本号是否是预发布
+下面例子给出两个版本号,然后通过 [is_prerelease](https://docs.rs/semver/1.0.7/semver/struct.Version.html#method.is_prerelease) 判断哪个是预发布的版本号。
+
+```rust,editable
+use semver::{Version, SemVerError};
+
+fn main() -> Result<(), SemVerError> {
+    let version_1 = Version::parse("1.0.0-alpha")?;
+    let version_2 = Version::parse("1.0.0")?;
+
+    assert!(version_1.is_prerelease());
+    assert!(!version_2.is_prerelease());
+
+    Ok(())
+}
+```
+
+### 找出给定范围内的最新版本
+下面例子给出了一个版本号列表,我们需要找到其中最新的版本。
+
+```rust,editable
+#use error_chain::error_chain;
+
+use semver::{Version, VersionReq};
+
+#error_chain! {
+#    foreign_links {
+#        SemVer(semver::SemVerError);
+#        SemVerReq(semver::ReqParseError);
+#    }
+3}
+
+fn find_max_matching_version<'a, I>(version_req_str: &str, iterable: I) -> Result<Option<Version>>
+where
+    I: IntoIterator<Item = &'a str>,
+{
+    let vreq = VersionReq::parse(version_req_str)?;
+
+    Ok(
+        iterable
+            .into_iter()
+            .filter_map(|s| Version::parse(s).ok())
+            .filter(|s| vreq.matches(s))
+            .max(),
+    )
+}
+
+fn main() -> Result<()> {
+    assert_eq!(
+        find_max_matching_version("<= 1.0.0", vec!["0.9.0", "1.0.0", "1.0.1"])?,
+        Some(Version::parse("1.0.0")?)
+    );
+
+    assert_eq!(
+        find_max_matching_version(
+            ">1.2.3-alpha.3",
+            vec![
+                "1.2.3-alpha.3",
+                "1.2.3-alpha.4",
+                "1.2.3-alpha.10",
+                "1.2.3-beta.4",
+                "3.4.5-alpha.9",
+            ]
+        )?,
+        Some(Version::parse("1.2.3-beta.4")?)
+    );
+
+    Ok(())
+}
+```
+
+### 检查外部命令的版本号兼容性
+下面将通过 [Command](https://doc.rust-lang.org/std/process/struct.Command.html) 来执行系统命令 `git --version`,并对该系统命令返回的 `git` 版本号进行解析。
+
+```rust,editable
+#use error_chain::error_chain;
+
+use std::process::Command;
+use semver::{Version, VersionReq};
+
+#error_chain! {
+#    foreign_links {
+#        Io(std::io::Error);
+#        Utf8(std::string::FromUtf8Error);
+#        SemVer(semver::SemVerError);
+#        SemVerReq(semver::ReqParseError);
+#    }
+#}
+
+fn main() -> Result<()> {
+    let version_constraint = "> 1.12.0";
+    let version_test = VersionReq::parse(version_constraint)?;
+    let output = Command::new("git").arg("--version").output()?;
+
+    if !output.status.success() {
+        error_chain::bail!("Command executed with failing error code");
+    }
+
+    let stdout = String::from_utf8(output.stdout)?;
+    let version = stdout.split(" ").last().ok_or_else(|| {
+        "Invalid command output"
+    })?;
+    let parsed_version = Version::parse(version)?;
+
+    if !version_test.matches(&parsed_version) {
+        error_chain::bail!("Command version lower than minimum supported version (found {}, need {})",
+            parsed_version, version_constraint);
+    }
+
+    Ok(())
+}
+```
\ No newline at end of file

From 327e7c42f0b616fd4b67ee6b5de4f6032a7fa5e4 Mon Sep 17 00:00:00 2001
From: sunface <cto@188.com>
Date: Mon, 4 Apr 2022 22:51:30 +0800
Subject: [PATCH 24/34] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AB=A0=20[Cookbook?=
 =?UTF-8?q?=20-=20=E7=89=88=E6=9C=AC=E5=8F=B7]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/SUMMARY.md                   | 2 +-
 src/cookbook/devtools/version.md | 2 +-
 内容变更记录.md            | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index ab32cbe2..51a325e9 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -190,7 +190,7 @@
   - [开发者工具]()
     - [日志](cookbook/devtools/log.md)
     - [配置日志](cookbook/devtools/config-log.md)
-    - [版本](cookbook/devtools/version.md)
+    - [版本号](cookbook/devtools/version.md)
     - [构建时工具](cookbook/devtools/build-tools.md)
 
 
diff --git a/src/cookbook/devtools/version.md b/src/cookbook/devtools/version.md
index 70ee24e3..2d9ff997 100644
--- a/src/cookbook/devtools/version.md
+++ b/src/cookbook/devtools/version.md
@@ -1,4 +1,4 @@
-# 版本
+# 版本号
 
 ### 解析并增加版本号
 下面例子使用 [Version::parse](https://docs.rs/semver/*/semver/struct.Version.html#method.parse) 将一个字符串转换成 [semver::Version](https://docs.rs/semver/*/semver/struct.Version.html) 版本号,然后将它的 patch, minor, major 版本号都增加 1。
diff --git a/内容变更记录.md b/内容变更记录.md
index 9b9b38ca..1cd8c8f0 100644
--- a/内容变更记录.md
+++ b/内容变更记录.md
@@ -8,7 +8,7 @@
 - 新增章节: [Cookbook - 哈希](https://course.rs/cookbook/cryptography/hashing.html)
 - 新增章节: [Cookbook - 位字段](https://course.rs/cookbook/datastructures/bitfield.html)
 - 新增章节: [Cookbook - 数据库](https://course.rs/cookbook/database/sqlite.html)
-
+- 新增章节: [Cookbook - 开发者工具](https://course.rs/cookbook/devtools/log.html)
 
 ## 2022-04-03
 

From ff35468af6f43ae19a3c87b394a423d43709003d Mon Sep 17 00:00:00 2001
From: sunface <cto@188.com>
Date: Mon, 4 Apr 2022 23:22:03 +0800
Subject: [PATCH 25/34] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AB=A0=20[Cookbook?=
 =?UTF-8?q?=20-=20=E6=9E=84=E5=BB=BA=E5=B7=A5=E5=85=B7]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/cookbook/devtools/build-tools.md | 191 +++++++++++++++++++++++++++
 1 file changed, 191 insertions(+)

diff --git a/src/cookbook/devtools/build-tools.md b/src/cookbook/devtools/build-tools.md
index d5694492..b3bdc6ad 100644
--- a/src/cookbook/devtools/build-tools.md
+++ b/src/cookbook/devtools/build-tools.md
@@ -1 +1,192 @@
 # 构建时工具
+本章节的内容是关于构建工具的,如果大家没有听说过 `build.rs` 文件,强烈建议先看看[这里](https://course.rs/cargo/reference/build-script/intro.html)了解下何为构建工具。
+
+### 编译并静态链接一个 C 库
+
+[cc](https://docs.rs/cc/latest/cc/) 包能帮助我们更好地跟 C/C++/汇编进行交互:它提供了简单的 API 可以将外部的库编译成静态库( .a ),然后通过 `rustc` 进行静态链接。
+
+下面的例子中,我们将在 Rust 代码中使用 C 的代码: *src/hello.c*。在开始编译 Rust 的项目代码前,`build.rs` 构建脚本将先被执行。通过 cc 包,一个静态的库可以被生成( *libhello.a* ),然后该库将被 Rust的代码所使用:通过 `extern` 声明外部函数签名的方式来使用。
+
+由于例子中的 C 代码很简单,因此只需要将一个文件传递给 [cc::Build](https://docs.rs/cc/*/cc/struct.Build.html)。如果大家需要更复杂的构建,`cc::Build` 还提供了通过 [include](https://docs.rs/cc/*/cc/struct.Build.html#method.include) 来包含路径的方式,以及额外的编译标志( [flags](https://docs.rs/cc/1.0.73/cc/struct.Build.html#method.flag) )。
+
+*Cargo.toml*
+
+```toml
+[package]
+...
+build = "build.rs"
+
+[build-dependencies]
+cc = "1"
+
+[dependencies]
+error-chain = "0.11"
+```
+
+*build.rs*
+
+```rust
+fn main() {
+    cc::Build::new()
+        .file("src/hello.c")
+        .compile("hello");   // outputs `libhello.a`
+}
+```
+
+*src/hello.c*
+
+```C
+#include <stdio.h>
+
+
+void hello() {
+    printf("Hello from C!\n");
+}
+
+void greet(const char* name) {
+    printf("Hello, %s!\n", name);
+}
+```
+
+*src/main.rs*
+
+```rust
+use error_chain::error_chain;
+use std::ffi::CString;
+use std::os::raw::c_char;
+
+error_chain! {
+    foreign_links {
+        NulError(::std::ffi::NulError);
+        Io(::std::io::Error);
+    }
+}
+fn prompt(s: &str) -> Result<String> {
+    use std::io::Write;
+    print!("{}", s);
+    std::io::stdout().flush()?;
+    let mut input = String::new();
+    std::io::stdin().read_line(&mut input)?;
+    Ok(input.trim().to_string())
+}
+
+extern {
+    fn hello();
+    fn greet(name: *const c_char);
+}
+
+fn main() -> Result<()> {
+    unsafe { hello() }
+    let name = prompt("What's your name? ")?;
+    let c_name = CString::new(name)?;
+    unsafe { greet(c_name.as_ptr()) }
+    Ok(())
+}
+```
+
+###  编译并静态链接一个 C++ 库
+链接到 C++ 库跟之前的方式非常相似。主要的区别在于链接到 C++ 库时,你需要通过构建方法 [cpp(true)](https://docs.rs/cc/*/cc/struct.Build.html#method.cpp) 来指定一个 C++ 编译器,然后在 C++ 的代码顶部添加 `extern "C"` 来阻止 C++ 编译器对库名进行名称重整( name mangling )。
+
+*Cargo.toml*
+
+```toml
+[package]
+...
+build = "build.rs"
+
+[build-dependencies]
+cc = "1"
+```
+
+*build.rs*
+
+```rust
+fn main() {
+    cc::Build::new()
+        .cpp(true)
+        .file("src/foo.cpp")
+        .compile("foo");   
+}
+```
+
+*src/foo.cpp*
+
+```c++
+extern "C" {
+    int multiply(int x, int y);
+}
+
+int multiply(int x, int y) {
+    return x*y;
+}
+```
+
+*src/main.rs*
+
+```rust
+extern {
+    fn multiply(x : i32, y : i32) -> i32;
+}
+
+fn main(){
+    unsafe {
+        println!("{}", multiply(5,7));
+    }   
+}
+```
+
+### 为 C 库创建自定义的 define
+
+[cc::Build::define](https://docs.rs/cc/*/cc/struct.Build.html#method.define) 可以让我们使用自定义的 define 来构建 C 库。
+
+以下示例在构建脚本 `build.rs` 中动态定义了一个 define,然后在运行时打印出 **Welcome to foo - version 1.0.2**。Cargo 会设置一些[环境变量](https://doc.rust-lang.org/cargo/reference/environment-variables.html),它们对于自定义的 define 会有所帮助。
+
+*Cargo.toml*
+
+```toml
+[package]
+...
+version = "1.0.2"
+build = "build.rs"
+
+[build-dependencies]
+cc = "1"
+```
+
+*build.rs*
+```rust
+fn main() {
+    cc::Build::new()
+        .define("APP_NAME", "\"foo\"")
+        .define("VERSION", format!("\"{}\"", env!("CARGO_PKG_VERSION")).as_str())
+        .define("WELCOME", None)
+        .file("src/foo.c")
+        .compile("foo");
+}
+```
+
+*src/foo.c*
+```C
+#include <stdio.h>
+
+void print_app_info() {
+#ifdef WELCOME
+    printf("Welcome to ");
+#endif
+    printf("%s - version %s\n", APP_NAME, VERSION);
+}
+```
+
+*src/main.rs*
+```rust
+extern {
+    fn print_app_info();
+}
+
+fn main(){
+    unsafe {
+        print_app_info();
+    }   
+}
+```
+

From 96834bf18be0b480885923c7361b2d0308e3d277 Mon Sep 17 00:00:00 2001
From: sunface <cto@188.com>
Date: Tue, 5 Apr 2022 13:26:06 +0800
Subject: [PATCH 26/34] update readme.md

---
 README.md                               |   4 +-
 src/SUMMARY.md                          |  30 +--
 src/cookbook/algos/intro.md             |   4 -
 src/cookbook/algos/randomness.md        | 155 -----------
 src/cookbook/algos/sorting.md           |  84 ------
 src/cookbook/cmd/ansi.md                |  50 ----
 src/cookbook/cmd/parsing.md             |  70 -----
 src/cookbook/cocurrency/parallel.md     | 201 --------------
 src/cookbook/cocurrency/threads.md      | 336 ------------------------
 src/cookbook/compression/tar.md         |  77 ------
 src/cookbook/config.md                  |   1 -
 src/cookbook/crypto.md                  |   1 -
 src/cookbook/cryptography/encryption.md |  57 ----
 src/cookbook/cryptography/hashing.md    |  71 -----
 src/cookbook/database.md                |   1 -
 src/cookbook/database/postgres.md       | 123 ---------
 src/cookbook/database/sqlite.md         | 136 ----------
 src/cookbook/datastructures/bitfield.md |  47 ----
 src/cookbook/date.md                    |   1 -
 src/cookbook/datetime/duration.md       |  69 -----
 src/cookbook/datetime/parsing.md        | 114 --------
 src/cookbook/dev/intro.md               |   1 -
 src/cookbook/dev/logs.md                |   1 -
 src/cookbook/dev/profile.md             |   1 -
 src/cookbook/devtools/build-tools.md    | 192 --------------
 src/cookbook/devtools/config-log.md     | 140 ----------
 src/cookbook/devtools/log.md            | 127 ---------
 src/cookbook/devtools/version.md        | 186 -------------
 src/cookbook/encoding/csv.md            |   1 -
 src/cookbook/encoding/intro.md          |   1 -
 src/cookbook/encoding/json.md           |   1 -
 src/cookbook/encoding/protobuf.md       |   1 -
 src/cookbook/file/dir.md                |   1 -
 src/cookbook/file/file.md               |   1 -
 src/cookbook/file/intro.md              |   1 -
 src/cookbook/intro.md                   |   6 +
 src/cookbook/protocol/grpc.md           |   1 -
 src/cookbook/protocol/http.md           |   1 -
 src/cookbook/protocol/intro.md          |   1 -
 src/cookbook/protocol/tcp.md            |   1 -
 src/cookbook/protocol/udp.md            |   1 -
 src/cookbook/regexp.md                  |   1 -
 42 files changed, 9 insertions(+), 2290 deletions(-)
 delete mode 100644 src/cookbook/algos/intro.md
 delete mode 100644 src/cookbook/algos/randomness.md
 delete mode 100644 src/cookbook/algos/sorting.md
 delete mode 100644 src/cookbook/cmd/ansi.md
 delete mode 100644 src/cookbook/cmd/parsing.md
 delete mode 100644 src/cookbook/cocurrency/parallel.md
 delete mode 100644 src/cookbook/cocurrency/threads.md
 delete mode 100644 src/cookbook/compression/tar.md
 delete mode 100644 src/cookbook/config.md
 delete mode 100644 src/cookbook/crypto.md
 delete mode 100644 src/cookbook/cryptography/encryption.md
 delete mode 100644 src/cookbook/cryptography/hashing.md
 delete mode 100644 src/cookbook/database.md
 delete mode 100644 src/cookbook/database/postgres.md
 delete mode 100644 src/cookbook/database/sqlite.md
 delete mode 100644 src/cookbook/datastructures/bitfield.md
 delete mode 100644 src/cookbook/date.md
 delete mode 100644 src/cookbook/datetime/duration.md
 delete mode 100644 src/cookbook/datetime/parsing.md
 delete mode 100644 src/cookbook/dev/intro.md
 delete mode 100644 src/cookbook/dev/logs.md
 delete mode 100644 src/cookbook/dev/profile.md
 delete mode 100644 src/cookbook/devtools/build-tools.md
 delete mode 100644 src/cookbook/devtools/config-log.md
 delete mode 100644 src/cookbook/devtools/log.md
 delete mode 100644 src/cookbook/devtools/version.md
 delete mode 100644 src/cookbook/encoding/csv.md
 delete mode 100644 src/cookbook/encoding/intro.md
 delete mode 100644 src/cookbook/encoding/json.md
 delete mode 100644 src/cookbook/encoding/protobuf.md
 delete mode 100644 src/cookbook/file/dir.md
 delete mode 100644 src/cookbook/file/file.md
 delete mode 100644 src/cookbook/file/intro.md
 delete mode 100644 src/cookbook/protocol/grpc.md
 delete mode 100644 src/cookbook/protocol/http.md
 delete mode 100644 src/cookbook/protocol/intro.md
 delete mode 100644 src/cookbook/protocol/tcp.md
 delete mode 100644 src/cookbook/protocol/udp.md
 delete mode 100644 src/cookbook/regexp.md

diff --git a/README.md b/README.md
index 45482ff4..25b9919d 100644
--- a/README.md
+++ b/README.md
@@ -31,9 +31,9 @@
 
 - **规避陷阱和对抗编译器**,只有真的上手写过一长段时间 Rust 项目,才知道该如何规避常见的陷阱以及解决一些难搞的编译器错误,而本书将帮助你大大缩短这个过程,提前规避这些问题
 
-- **Cook Book**,涵盖多个应用场景的实战代码片段,程序员上网查询文件操作、正则解析、数据库操作是常事,没有人能记住所有代码,而 Cook Book 可解君忧,Ctrl + C/V 走天下
+- **[Cookbook](https://cookbook.rs)**,涵盖多个应用场景的实战代码片段,程序员上网查询文件操作、正则解析、数据库操作是常事,没有人能记住所有代码,而 Cook Book 可解君忧,Ctrl + C/V 走天下
 
-- **配套练习题**,像学习一门大学课程一样学习 Rust 是一种什么感觉?*Rust语言圣经 + [Rust语言实战](https://github.com/sunface/rust-by-practice)* 双剑合璧,给你最极致的学习体验
+- **[配套练习题](https://github.com/sunface/rust-by-practice)**,像学习一门大学课程一样学习 Rust 是一种什么感觉?*Rust语言圣经 + Rust语言实战* 双剑合璧,给你最极致的学习体验
 
 总之在写作过程中我们始终铭记初心:为中国用户打造一门**全面的、深入的、持续更新的** Rust 教程。 新手用来入门,老手用来提高,高手用来提升生产力。
 
diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index 51a325e9..7358fe75 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -164,35 +164,7 @@
 
 # 应用实战
 ---
-- [CookBook doing](cookbook/intro.md)
-  - [实用算法](cookbook/algos/intro.md)
-    - [生成随机值](cookbook/algos/randomness.md)
-    - [Vec 排序](cookbook/algos/sorting.md)
-  - [命令行]()
-    - [参数解析](cookbook/cmd/parsing.md)
-    - [终端输出格式化](cookbook/cmd/ansi.md)
-  - [压缩]()
-    - [使用.tar包](cookbook/compression/tar.md)
-  - [并发]()
-    - [线程](cookbook/cocurrency/threads.md)
-    - [使用rayon并行处理数据](cookbook/cocurrency/parallel.md)
-  - [密码学]()
-    - [哈希](cookbook/cryptography/hashing.md)
-    - [加密](cookbook/cryptography/encryption.md)
-  - [数据结构]()
-    - [位字段](cookbook/datastructures/bitfield.md)
-  - [数据库]()
-    - [SQLite](cookbook/database/sqlite.md)
-    - [Postgres](cookbook/database/postgres.md)
-  - [日期和时间]()
-    - [时间计算和转换](cookbook/datetime/duration.md)
-    - [解析和显示](cookbook/datetime/parsing.md)
-  - [开发者工具]()
-    - [日志](cookbook/devtools/log.md)
-    - [配置日志](cookbook/devtools/config-log.md)
-    - [版本号](cookbook/devtools/version.md)
-    - [构建时工具](cookbook/devtools/build-tools.md)
-
+- [CookBook](cookbook/intro.md)
 
 - [Rust 最佳实践](practice/intro.md)
   - [对抗编译检查](practice/fight-with-compiler/intro.md)
diff --git a/src/cookbook/algos/intro.md b/src/cookbook/algos/intro.md
deleted file mode 100644
index ace373bf..00000000
--- a/src/cookbook/algos/intro.md
+++ /dev/null
@@ -1,4 +0,0 @@
-# 实用算法
-本章将收集一些在实战中经常使用的算法 API。
-
-> Note: 这里没有具体的算法实现,都是关于如何应用的
\ No newline at end of file
diff --git a/src/cookbook/algos/randomness.md b/src/cookbook/algos/randomness.md
deleted file mode 100644
index 04241a7e..00000000
--- a/src/cookbook/algos/randomness.md
+++ /dev/null
@@ -1,155 +0,0 @@
-# 生成随机值
-
-### 生成随机数
-
-使用 [rand::thread_rng](https://docs.rs/rand/*/rand/fn.thread_rng.html) 可以获取一个随机数生成器 [rand::Rng](https://docs.rs/rand/0.8.5/rand/trait.Rng.html) ,该生成器需要在每个线程都初始化一个。
-
-整数的随机分布范围等于类型的取值范围,但是浮点数只分布在 `[0, 1)` 区间内。
-
-```rust,editable
-use rand::Rng;
-
-fn main() {
-    let mut rng = rand::thread_rng();
-
-    let n1: u8 = rng.gen();
-    let n2: u16 = rng.gen();
-    println!("Random u8: {}", n1);
-    println!("Random u16: {}", n2);
-    println!("Random u32: {}", rng.gen::<u32>());
-    println!("Random i32: {}", rng.gen::<i32>());
-    println!("Random float: {}", rng.gen::<f64>());
-}
-```
-
-### 指定范围生成随机数
-
-使用 [Rng::gen_range](https://rust-lang-nursery.github.io/rust-cookbook/algorithms/randomness.html) 生成 [0, 10) 区间内的随机数( 右开区间,不包括 `10` )。
-```rust,editable
-use rand::Rng;
-
-fn main() {
-    let mut rng = rand::thread_rng();
-    println!("Integer: {}", rng.gen_range(0..10));
-    println!("Float: {}", rng.gen_range(0.0..10.0));
-}
-```
-
-[Uniform](https://docs.rs/rand/*/rand/distributions/uniform/struct.Uniform.html) 可以用于生成<ruby>均匀分布<rt>uniform distribution</rt></ruby>的随机数。当需要在同一个范围内重复生成随机数时,该方法虽然和之前的方法效果一样,但会更快一些。
-
-```rust,editable
-use rand::distributions::{Distribution, Uniform};
-
-fn main() {
-    let mut rng = rand::thread_rng();
-    let die = Uniform::from(1..7);
-
-    loop {
-        let throw = die.sample(&mut rng);
-        println!("Roll the die: {}", throw);
-        if throw == 6 {
-            break;
-        }
-    }
-}
-```
-
-### 使用指定分布来生成随机数
-
-默认情况下,`rand` 包使用均匀分布来生成随机数,而 [rand_distr](https://docs.rs/rand_distr/*/rand_distr/index.html) 包提供了其它类型的分布方式。
-
-首先,你需要获取想要使用的分布的实例,然后在 [rand::Rng](https://docs.rs/rand/*/rand/trait.Rng.html) 的帮助下使用 [Distribution::sample](https://docs.rs/rand/*/rand/distributions/trait.Distribution.html#tymethod.sample) 对该实例进行取样。
-
-如果想要查询可用的分布列表,可以访问[这里](https://docs.rs/rand_distr/*/rand_distr/index.html),下面的示例中我们将使用 [Normal](https://docs.rs/rand_distr/0.4.3/rand_distr/struct.Normal.html) 分布:
-```rust,editable
-use rand_distr::{Distribution, Normal, NormalError};
-use rand::thread_rng;
-
-fn main() -> Result<(), NormalError> {
-    let mut rng = thread_rng();
-    let normal = Normal::new(2.0, 3.0)?;
-    let v = normal.sample(&mut rng);
-    println!("{} is from a N(2, 9) distribution", v);
-    Ok(())
-}
-```
-
-### 在自定义类型中生成随机值
-
-
-使用 [Distribution](https://docs.rs/rand/*/rand/distributions/trait.Distribution.html) 特征包裹我们的自定义类型,并为 [Standard](https://docs.rs/rand/*/rand/distributions/struct.Standard.html) 实现该特征,可以为自定义类型的指定字段生成随机数。
-
-
-```rust,editable
-use rand::Rng;
-use rand::distributions::{Distribution, Standard};
-
-#[derive(Debug)]
-struct Point {
-    x: i32,
-    y: i32,
-}
-
-impl Distribution<Point> for Standard {
-    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Point {
-        let (rand_x, rand_y) = rng.gen();
-        Point {
-            x: rand_x,
-            y: rand_y,
-        }
-    }
-}
-
-fn main() {
-    let mut rng = rand::thread_rng();
-
-    // 生成一个随机的 Point
-    let rand_point: Point = rng.gen();
-    println!("Random Point: {:?}", rand_point);
-
-    // 通过类型暗示( hint )生成一个随机的元组
-    let rand_tuple = rng.gen::<(i32, bool, f64)>();
-    println!("Random tuple: {:?}", rand_tuple);
-}
-```
-
-### 生成随机的字符串(A-Z, a-z, 0-9)
-通过 [Alphanumeric](https://docs.rs/rand/0.8.5/rand/distributions/struct.Alphanumeric.html) 采样来生成随机的 ASCII 字符串,包含从 `A-Z, a-z, 0-9` 的字符。
-
-```rust,editble
-use rand::{thread_rng, Rng};
-use rand::distributions::Alphanumeric;
-
-fn main() {
-    let rand_string: String = thread_rng()
-        .sample_iter(&Alphanumeric)
-        .take(30)
-        .map(char::from)
-        .collect();
-
-    println!("{}", rand_string);
-}
-```
-
-### 生成随机的字符串( 用户指定 ASCII 字符 )
-通过 [gen_string](https://docs.rs/rand/0.8.5/rand/trait.Rng.html#method.gen_range) 生成随机的 ASCII 字符串,包含用户指定的字符。
-
-```rust,editable
-fn main() {
-    use rand::Rng;
-    const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
-                            abcdefghijklmnopqrstuvwxyz\
-                            0123456789)(*&^%$#@!~";
-    const PASSWORD_LEN: usize = 30;
-    let mut rng = rand::thread_rng();
-
-    let password: String = (0..PASSWORD_LEN)
-        .map(|_| {
-            let idx = rng.gen_range(0..CHARSET.len());
-            CHARSET[idx] as char
-        })
-        .collect();
-
-    println!("{:?}", password);
-}
-```
diff --git a/src/cookbook/algos/sorting.md b/src/cookbook/algos/sorting.md
deleted file mode 100644
index 76e44e8c..00000000
--- a/src/cookbook/algos/sorting.md
+++ /dev/null
@@ -1,84 +0,0 @@
-## Vector 排序
-
-
-### 对整数 Vector 排序
-
-以下示例使用 [Vec::sort](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.sort) 来排序,如果大家希望获得更高的性能,可以使用 [Vec::sort_unstable](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.sort_unstable),但是该方法无法保留相等元素的顺序。
-
-```rust,editable
-fn main() {
-    let mut vec = vec![1, 5, 10, 2, 15];
-    
-    vec.sort();
-
-    assert_eq!(vec, vec![1, 2, 5, 10, 15]);
-}
-```
-
-### 对浮点数 Vector 排序
-
-浮点数数组可以使用 [Vec::sort_by](https://doc.rust-lang.org/std/primitive.slice.html#method.sort_by) 和 [PartialOrd::partial_cmp](https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html#tymethod.partial_cmp) 进行排序。
-
-```rust,editable
-fn main() {
-    let mut vec = vec![1.1, 1.15, 5.5, 1.123, 2.0];
-
-    vec.sort_by(|a, b| a.partial_cmp(b).unwrap());
-
-    assert_eq!(vec, vec![1.1, 1.123, 1.15, 2.0, 5.5]);
-}
-```
-
-### 对结构体 Vector 排序
-
-以下示例中的结构体 `Person` 将实现基于字段 `name` 和 `age` 的自然排序。为了让 `Person` 变为可排序的,我们需要为其派生 `Eq、PartialEq、Ord、PartialOrd` 特征,关于这几个特征的详情,请见[这里](https://course.rs/advance/confonding/eq.html)。
-
-当然,还可以使用 [vec:sort_by](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.sort_by) 方法配合一个自定义比较函数,只按照 `age` 的维度对 `Person` 数组排序。
-
-```rust,editable
-#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
-struct Person {
-    name: String,
-    age: u32
-}
-
-impl Person {
-    pub fn new(name: String, age: u32) -> Self {
-        Person {
-            name,
-            age
-        }
-    }
-}
-
-fn main() {
-    let mut people = vec![
-        Person::new("Zoe".to_string(), 25),
-        Person::new("Al".to_string(), 60),
-        Person::new("John".to_string(), 1),
-    ];
-
-    // 通过派生后的自然顺序(Name and age)排序
-    people.sort();
-
-    assert_eq!(
-        people,
-        vec![
-            Person::new("Al".to_string(), 60),
-            Person::new("John".to_string(), 1),
-            Person::new("Zoe".to_string(), 25),
-        ]);
-
-    // 只通过 age 排序
-    people.sort_by(|a, b| b.age.cmp(&a.age));
-
-    assert_eq!(
-        people,
-        vec![
-            Person::new("Al".to_string(), 60),
-            Person::new("Zoe".to_string(), 25),
-            Person::new("John".to_string(), 1),
-        ]);
-
-}
-```
\ No newline at end of file
diff --git a/src/cookbook/cmd/ansi.md b/src/cookbook/cmd/ansi.md
deleted file mode 100644
index 87c18682..00000000
--- a/src/cookbook/cmd/ansi.md
+++ /dev/null
@@ -1,50 +0,0 @@
-# ANSI 终端
-
-[ansi_term](https://crates.io/crates/ansi_term) 包可以帮我们控制终端上的输出样式,例如使用颜色文字、控制输出格式等,当然,前提是在 ANSI 终端上。
-
-`ansi_term` 中有两个主要数据结构:[ANSIString](https://docs.rs/ansi_term/0.12.1/ansi_term/type.ANSIString.html) 和 [Style](https://docs.rs/ansi_term/0.12.1/ansi_term/struct.Style.html)。
-
-`Style` 用于控制样式:颜色、加粗、闪烁等,而前者是一个带有样式的字符串。
-
-## 颜色字体
-
-```rust,editable
-use ansi_term::Colour;
-
-fn main() {
-    println!("This is {} in color, {} in color and {} in color",
-             Colour::Red.paint("red"),
-             Colour::Blue.paint("blue"),
-             Colour::Green.paint("green"));
-}
-```
-
-## 加粗字体
-
-比颜色复杂的样式构建需要使用 `Style` 结构体:
-```rust,editable
-use ansi_term::Style;
-
-fn main() {
-    println!("{} and this is not",
-             Style::new().bold().paint("This is Bold"));
-}
-```
-
-## 加粗和颜色
-
-`Colour` 实现了很多跟 `Style` 类似的函数,因此可以实现链式调用。
-
-```rust,editable
-use ansi_term::Colour;
-use ansi_term::Style;
-
-fn main(){
-    println!("{}, {} and {}",
-             Colour::Yellow.paint("This is colored"),
-             Style::new().bold().paint("this is bold"),
-             // Colour 也可以使用 bold 方法进行加粗
-             Colour::Yellow.bold().paint("this is bold and colored"));
-}
-```
-
diff --git a/src/cookbook/cmd/parsing.md b/src/cookbook/cmd/parsing.md
deleted file mode 100644
index b0356aef..00000000
--- a/src/cookbook/cmd/parsing.md
+++ /dev/null
@@ -1,70 +0,0 @@
-# 参数解析
-
-## Clap
-下面的程序给出了使用 `clap` 来解析命令行参数的样式结构,如果大家想了解更多,在 `clap` [文档](https://docs.rs/clap/)中还给出了另外两种初始化一个应用的方式。
-
-在下面的构建中,`value_of` 将获取通过 `with_name` 解析出的值。`short` 和 `long` 用于设置用户输入的长短命令格式,例如短命令 `-f` 和长命令 `--file`。
-
-```rust,editable
-use clap::{Arg, App};
-
-fn main() {
-    let matches = App::new("My Test Program")
-        .version("0.1.0")
-        .author("Hackerman Jones <hckrmnjones@hack.gov>")
-        .about("Teaches argument parsing")
-        .arg(Arg::with_name("file")
-                 .short("f")
-                 .long("file")
-                 .takes_value(true)
-                 .help("A cool file"))
-        .arg(Arg::with_name("num")
-                 .short("n")
-                 .long("number")
-                 .takes_value(true)
-                 .help("Five less than your favorite number"))
-        .get_matches();
-
-    let myfile = matches.value_of("file").unwrap_or("input.txt");
-    println!("The file passed is: {}", myfile);
-
-    let num_str = matches.value_of("num");
-    match num_str {
-        None => println!("No idea what your favorite number is."),
-        Some(s) => {
-            match s.parse::<i32>() {
-                Ok(n) => println!("Your favorite number must be {}.", n + 5),
-                Err(_) => println!("That's not a number! {}", s),
-            }
-        }
-    }
-}
-```
-
-`clap` 针对上面提供的构建样式,会自动帮我们生成相应的使用方式说明。例如,上面代码生成的使用说明如下:
-```shell
-My Test Program 0.1.0
-Hackerman Jones <hckrmnjones@hack.gov>
-Teaches argument parsing
-
-USAGE:
-    testing [OPTIONS]
-
-FLAGS:
-    -h, --help       Prints help information
-    -V, --version    Prints version information
-
-OPTIONS:
-    -f, --file <file>     A cool file
-    -n, --number <num>    Five less than your favorite number
-```
-
-最后,再使用一些参数来运行下我们的代码:
-```shell
-$ cargo run -- -f myfile.txt -n 251
-The file passed is: myfile.txt
-Your favorite number must be 256.
-```
-
-## Structopt
-@todo
\ No newline at end of file
diff --git a/src/cookbook/cocurrency/parallel.md b/src/cookbook/cocurrency/parallel.md
deleted file mode 100644
index d1d5c521..00000000
--- a/src/cookbook/cocurrency/parallel.md
+++ /dev/null
@@ -1,201 +0,0 @@
-# 任务并行处理
-
-### 并行修改数组中的元素
-
-[rayon](https://docs.rs/rayon/1.5.1/rayon/index.html) 提供了一个 [par_iter_mut](https://docs.rs/rayon/*/rayon/iter/trait.IntoParallelRefMutIterator.html#tymethod.par_iter_mut) 方法用于并行化迭代一个数据集合。
-
-```rust,editable
-use rayon::prelude::*;
-
-fn main() {
-    let mut arr = [0, 7, 9, 11];
-    arr.par_iter_mut().for_each(|p| *p -= 1);
-    println!("{:?}", arr);
-}
-```
-
-### 并行测试集合中的元素是否满足给定的条件
-
-[rayon::any](https://docs.rs/rayon/*/rayon/iter/trait.ParallelIterator.html#method.any) 和 [rayon::all](https://docs.rs/rayon/*/rayon/iter/trait.ParallelIterator.html#method.all) 类似于 [std::any](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.any) / [std::all](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.all) ,但是是并行版本的。
-
-- `rayon::any` 并行检查迭代器中是否有任何元素满足给定的条件,一旦发现符合条件的元素,就立即返回
-- `rayon::all` 并行检查迭代器中的所有元素是否满足给定的条件,一旦发现不满足条件的元素,就立即返回
-
-```rust,editable
-use rayon::prelude::*;
-
-fn main() {
-    let mut vec = vec![2, 4, 6, 8];
-
-    assert!(!vec.par_iter().any(|n| (*n % 2) != 0));
-    assert!(vec.par_iter().all(|n| (*n % 2) == 0));
-    assert!(!vec.par_iter().any(|n| *n > 8 ));
-    assert!(vec.par_iter().all(|n| *n <= 8 ));
-
-    vec.push(9);
-
-    assert!(vec.par_iter().any(|n| (*n % 2) != 0));
-    assert!(!vec.par_iter().all(|n| (*n % 2) == 0));
-    assert!(vec.par_iter().any(|n| *n > 8 ));
-    assert!(!vec.par_iter().all(|n| *n <= 8 )); 
-}
-```
-
-### 使用给定条件并行搜索
-下面例子使用 [par_iter](https://docs.rs/rayon/*/rayon/iter/trait.IntoParallelRefIterator.html#tymethod.par_iter) 和 [rayon::find_any](https://docs.rs/rayon/*/rayon/iter/trait.ParallelIterator.html#method.find_any) 来并行搜索一个数组,直到找到任意一个满足条件的元素。
-
-如果有多个元素满足条件,`rayon` 会返回第一个找到的元素,注意:第一个找到的元素未必是数组中的顺序最靠前的那个。
-
-```rust,editable
-use rayon::prelude::*;
-
-fn main() {
-    let v = vec![6, 2, 1, 9, 3, 8, 11];
-
-    // 这里使用了 `&&x` 的形式,大家可以在以下链接阅读更多 https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.find
-    let f1 = v.par_iter().find_any(|&&x| x == 9);
-    let f2 = v.par_iter().find_any(|&&x| x % 2 == 0 && x > 6);
-    let f3 = v.par_iter().find_any(|&&x| x > 8);
-
-    assert_eq!(f1, Some(&9));
-    assert_eq!(f2, Some(&8));
-    assert!(f3 > Some(&8));
-}
-```
-
-### 对数组进行并行排序
-下面的例子将对字符串数组进行并行排序。
-
-[par_sort_unstable](https://docs.rs/rayon/*/rayon/slice/trait.ParallelSliceMut.html#method.par_sort_unstable) 方法的排序性能往往要比[稳定的排序算法](https://docs.rs/rayon/1.5.1/rayon/slice/trait.ParallelSliceMut.html#method.par_sort)更高。
-
-
-```rust,editable
-use rand::{Rng, thread_rng};
-use rand::distributions::Alphanumeric;
-use rayon::prelude::*;
-
-fn main() {
-  let mut vec = vec![String::new(); 100_000];
-  // 并行生成数组中的字符串
-  vec.par_iter_mut().for_each(|p| {
-    let mut rng = thread_rng();
-    *p = (0..5).map(|_| rng.sample(&Alphanumeric)).collect()
-  });
-  
-  // 
-  vec.par_sort_unstable();
-}
-```
-
-### 并行化 Map-Reuduce
-
-下面例子使用 [rayon::filter](https://docs.rs/rayon/*/rayon/iter/trait.ParallelIterator.html#method.filter), [rayon::map](https://docs.rs/rayon/*/rayon/iter/trait.ParallelIterator.html#method.map), 和 [rayon::reduce](https://docs.rs/rayon/*/rayon/iter/trait.ParallelIterator.html#method.reduce) 来超过 30 岁的 `Person` 的平均年龄。
-
-- `rayon::filter` 返回集合中所有满足给定条件的元素
-- `rayon::map` 对集合中的每一个元素执行一个操作,创建并返回新的迭代器,类似于[迭代器适配器](https://course.rs/advance/functional-programing/iterator.html#迭代器适配器)
-- `rayon::reduce` 则迭代器的元素进行不停的聚合运算,直到获取一个最终结果,这个结果跟例子中 `rayon::sum` 获取的结果是相同的
-
-```rust,editable
-use rayon::prelude::*;
-
-struct Person {
-    age: u32,
-}
-
-fn main() {
-    let v: Vec<Person> = vec![
-        Person { age: 23 },
-        Person { age: 19 },
-        Person { age: 42 },
-        Person { age: 17 },
-        Person { age: 17 },
-        Person { age: 31 },
-        Person { age: 30 },
-    ];
-
-    let num_over_30 = v.par_iter().filter(|&x| x.age > 30).count() as f32;
-    let sum_over_30 = v.par_iter()
-        .map(|x| x.age)
-        .filter(|&x| x > 30)
-        .reduce(|| 0, |x, y| x + y);
-
-    let alt_sum_30: u32 = v.par_iter()
-        .map(|x| x.age)
-        .filter(|&x| x > 30)
-        .sum();
-
-    let avg_over_30 = sum_over_30 as f32 / num_over_30;
-    let alt_avg_over_30 = alt_sum_30 as f32/ num_over_30;
-
-    assert!((avg_over_30 - alt_avg_over_30).abs() < std::f32::EPSILON);
-    println!("The average age of people older than 30 is {}", avg_over_30);
-}
-```
-
-### 并行生成缩略图
-下面例子将为目录中的所有图片并行生成缩略图,然后将结果存到新的目录 `thumbnails` 中。
-
-[glob::glob_with](https://docs.rs/glob/*/glob/fn.glob_with.html) 可以找出当前目录下的所有 `.jpg` 文件,`rayon` 通过 [DynamicImage::resize](https://docs.rs/image/*/image/enum.DynamicImage.html#method.resize) 来并行调整图片的大小。
-
-```rust,editable
-# use error_chain::error_chain;
-
-use std::path::Path;
-use std::fs::create_dir_all;
-
-# use error_chain::ChainedError;
-use glob::{glob_with, MatchOptions};
-use image::{FilterType, ImageError};
-use rayon::prelude::*;
-
-# error_chain! {
-#    foreign_links {
-#        Image(ImageError);
-#        Io(std::io::Error);
-#        Glob(glob::PatternError);
-#    }
-#}
-
-fn main() -> Result<()> {
-    let options: MatchOptions = Default::default();
-    // 找到当前目录中的所有 `jpg` 文件
-    let files: Vec<_> = glob_with("*.jpg", options)?
-        .filter_map(|x| x.ok())
-        .collect();
-
-    if files.len() == 0 {
-        error_chain::bail!("No .jpg files found in current directory");
-    }
-
-    let thumb_dir = "thumbnails";
-    create_dir_all(thumb_dir)?;
-
-    println!("Saving {} thumbnails into '{}'...", files.len(), thumb_dir);
-
-    let image_failures: Vec<_> = files
-        .par_iter()
-        .map(|path| {
-            make_thumbnail(path, thumb_dir, 300)
-                .map_err(|e| e.chain_err(|| path.display().to_string()))
-        })
-        .filter_map(|x| x.err())
-        .collect();
-
-    image_failures.iter().for_each(|x| println!("{}", x.display_chain()));
-
-    println!("{} thumbnails saved successfully", files.len() - image_failures.len());
-    Ok(())
-}
-
-fn make_thumbnail<PA, PB>(original: PA, thumb_dir: PB, longest_edge: u32) -> Result<()>
-where
-    PA: AsRef<Path>,
-    PB: AsRef<Path>,
-{
-    let img = image::open(original.as_ref())?;
-    let file_path = thumb_dir.as_ref().join(original);
-
-    Ok(img.resize(longest_edge, longest_edge, FilterType::Nearest)
-        .save(file_path)?)
-}
-```
\ No newline at end of file
diff --git a/src/cookbook/cocurrency/threads.md b/src/cookbook/cocurrency/threads.md
deleted file mode 100644
index b980f9ca..00000000
--- a/src/cookbook/cocurrency/threads.md
+++ /dev/null
@@ -1,336 +0,0 @@
-# 线程
-
-### 生成一个临时性的线程
-
-下面例子用到了 [crossbeam](cookbook/cocurrency/intro.md) 包,它提供了非常实用的、用于并发和并行编程的数据结构和函数。
-
-[Scope::spawn](https://docs.rs/crossbeam/*/crossbeam/thread/struct.Scope.html#method.spawn) 会生成一个被限定了作用域的线程,该线程最大的特点就是:它会在传给 [crossbeam::scope](https://docs.rs/crossbeam/0.8.1/crossbeam/fn.scope.html) 的闭包函数返回前先行结束。得益于这个特点,子线程的创建使用就像是本地闭包函数调用,因此生成的线程内部可以使用外部环境中的变量!
-
-
-```rust,editable
-fn main() {
-    let arr = &[1, 25, -4, 10];
-    let max = find_max(arr);
-    assert_eq!(max, Some(25));
-}
-
-// 将数组分成两个部分,并使用新的线程对它们进行处理
-fn find_max(arr: &[i32]) -> Option<i32> {
-    const THRESHOLD: usize = 2;
-  
-    if arr.len() <= THRESHOLD {
-        return arr.iter().cloned().max();
-    }
-
-    let mid = arr.len() / 2;
-    let (left, right) = arr.split_at(mid);
-  
-    crossbeam::scope(|s| {
-        let thread_l = s.spawn(|_| find_max(left));
-        let thread_r = s.spawn(|_| find_max(right));
-  
-        let max_l = thread_l.join().unwrap()?;
-        let max_r = thread_r.join().unwrap()?;
-  
-        Some(max_l.max(max_r))
-    }).unwrap()
-}
-```
-
-### 创建并行流水线
-下面我们使用 [crossbeam](https://docs.rs/crossbeam/latest/crossbeam/) 和 [crossbeam-channel](https://docs.rs/crossbeam-channel/*/crossbeam_channel/index.html) 来创建一个并行流水线:流水线的两端分别是数据源和数据下沉( sink ),在流水线中间,有两个工作线程会从源头接收数据,对数据进行并行处理,最后将数据下沉。
-
-- 消息通道( channel )是 [crossbeam_channel::bounded](https://docs.rs/crossbeam-channel/0.5.4/crossbeam_channel/fn.bounded.html),它只能缓存一条消息。当缓存满后,发送者继续调用 [crossbeam_channel::Sender::send] 发送消息时会阻塞,直到一个工作线程( 消费者 ) 拿走这条消息
-- 消费者获取消息时先到先得的策略,因此两个工作线程只有一个能取到消息,保证消息不会被重复消费、处理
-- 通过迭代器 [crossbeam_channel::Receiver::iter](https://docs.rs/crossbeam-channel/*/crossbeam_channel/struct.Receiver.html#method.iter) 读取消息会阻塞当前线程,直到新消息的到来或 channel 关闭
-- channel 只有在所有的发送者或消费者关闭后,才能被关闭。而其中一个消费者 `rcv2` 处于阻塞读取状态,无比被关闭,因此我们必须要关闭所有发送者: `drop(snd1);`  `drop(snd2)` ,这样 channel 关闭后,主线程的 `rcv2` 才能从阻塞状态退出,最后整个程序结束。大家还是迷惑的话,可以看看这篇[文章](https://course.rs/practice/pitfalls/main-with-channel-blocked.html)。
-
-```rust,editable
-extern crate crossbeam;
-extern crate crossbeam_channel;
-
-use std::thread;
-use std::time::Duration;
-use crossbeam_channel::bounded;
-
-fn main() {
-    let (snd1, rcv1) = bounded(1);
-    let (snd2, rcv2) = bounded(1);
-    let n_msgs = 4;
-    let n_workers = 2;
-
-    crossbeam::scope(|s| {
-        // 生产者线程
-        s.spawn(|_| {
-            for i in 0..n_msgs {
-                snd1.send(i).unwrap();
-                println!("Source sent {}", i);
-            }
- 
-            // 关闭其中一个发送者 snd1
-            // 该关闭操作对于结束最后的循环是必须的
-            drop(snd1);
-        });
-
-        // 通过两个线程并行处理
-        for _ in 0..n_workers {
-            // 从数据源接收数据,然后发送到下沉端
-            let (sendr, recvr) = (snd2.clone(), rcv1.clone());
-            // 生成单独的工作线程
-            s.spawn(move |_| {
-            thread::sleep(Duration::from_millis(500));
-                // 等待通道的关闭
-                for msg in recvr.iter() {
-                    println!("Worker {:?} received {}.",
-                             thread::current().id(), msg);
-                    sendr.send(msg * 2).unwrap();
-                }
-            });
-        }
-        // 关闭通道,如果不关闭,下沉端将永远无法结束循环
-        drop(snd2);
-
-        // 下沉端
-        for msg in rcv2.iter() {
-            println!("Sink received {}", msg);
-        }
-    }).unwrap();
-}
-```
-
-
-### 线程间传递数据
-
-下面我们来看看 [crossbeam-channel](https://docs.rs/crossbeam-channel/*/crossbeam_channel/index.html) 的单生产者单消费者( SPSC ) 使用场景。
-
-```rust,editable
-use std::{thread, time};
-use crossbeam_channel::unbounded;
-
-fn main() {
-    // unbounded 意味着 channel 可以存储任意多的消息
-    let (snd, rcv) = unbounded();
-    let n_msgs = 5;
-    crossbeam::scope(|s| {
-        s.spawn(|_| {
-            for i in 0..n_msgs {
-                snd.send(i).unwrap();
-                thread::sleep(time::Duration::from_millis(100));
-            }
-        });
-    }).unwrap();
-    for _ in 0..n_msgs {
-        let msg = rcv.recv().unwrap();
-        println!("Received {}", msg);
-    }
-}
-```
-
-### 维护全局可变的状态
-
-[lazy_static]() 会创建一个全局的静态引用( static ref ),该引用使用了 `Mutex` 以支持可变性,因此我们可以在代码中对其进行修改。`Mutex` 能保证该全局状态同时只能被一个线程所访问。
-
-```rust,editable
-use error_chain::error_chain;
-use lazy_static::lazy_static;
-use std::sync::Mutex;
-
-error_chain!{ }
-
-lazy_static! {
-    static ref FRUIT: Mutex<Vec<String>> = Mutex::new(Vec::new());
-}
-
-fn insert(fruit: &str) -> Result<()> {
-    let mut db = FRUIT.lock().map_err(|_| "Failed to acquire MutexGuard")?;
-    db.push(fruit.to_string());
-    Ok(())
-}
-
-fn main() -> Result<()> {
-    insert("apple")?;
-    insert("orange")?;
-    insert("peach")?;
-    {
-        let db = FRUIT.lock().map_err(|_| "Failed to acquire MutexGuard")?;
-
-        db.iter().enumerate().for_each(|(i, item)| println!("{}: {}", i, item));
-    }
-    insert("grape")?;
-    Ok(())
-}
-```
-
-### 并行计算 iso 文件的 SHA256
-
-下面的示例将为当前目录中的每一个 .iso 文件都计算一个 SHA256 sum。其中线程池中会初始化和 CPU 核心数一致的线程数,其中核心数是通过 [num_cpus::get](https://docs.rs/num_cpus/*/num_cpus/fn.get.html) 函数获取。
-
-`Walkdir::new` 可以遍历当前的目录,然后调用 `execute` 来执行读操作和 SHA256 哈希计算。
-
-```rust,editable
-
-use walkdir::WalkDir;
-use std::fs::File;
-use std::io::{BufReader, Read, Error};
-use std::path::Path;
-use threadpool::ThreadPool;
-use std::sync::mpsc::channel;
-use ring::digest::{Context, Digest, SHA256};
-
-// Verify the iso extension
-fn is_iso(entry: &Path) -> bool {
-    match entry.extension() {
-        Some(e) if e.to_string_lossy().to_lowercase() == "iso" => true,
-        _ => false,
-    }
-}
-
-fn compute_digest<P: AsRef<Path>>(filepath: P) -> Result<(Digest, P), Error> {
-    let mut buf_reader = BufReader::new(File::open(&filepath)?);
-    let mut context = Context::new(&SHA256);
-    let mut buffer = [0; 1024];
-
-    loop {
-        let count = buf_reader.read(&mut buffer)?;
-        if count == 0 {
-            break;
-        }
-        context.update(&buffer[..count]);
-    }
-
-    Ok((context.finish(), filepath))
-}
-
-fn main() -> Result<(), Error> {
-    let pool = ThreadPool::new(num_cpus::get());
-
-    let (tx, rx) = channel();
-
-    for entry in WalkDir::new("/home/user/Downloads")
-        .follow_links(true)
-        .into_iter()
-        .filter_map(|e| e.ok())
-        .filter(|e| !e.path().is_dir() && is_iso(e.path())) {
-            let path = entry.path().to_owned();
-            let tx = tx.clone();
-            pool.execute(move || {
-                let digest = compute_digest(path);
-                tx.send(digest).expect("Could not send data!");
-            });
-        }
-
-    drop(tx);
-    for t in rx.iter() {
-        let (sha, path) = t?;
-        println!("{:?} {:?}", sha, path);
-    }
-    Ok(())
-}
-```
-
-
-### 使用线程池来绘制分形
-
-下面例子中将基于 [Julia Set]() 来绘制一个分形图片,其中使用到了线程池来做分布式计算。
-
-<img src="https://cloud.githubusercontent.com/assets/221000/26546700/9be34e80-446b-11e7-81dc-dd9871614ea1.png" />
-
-```rust,edtiable
-# use error_chain::error_chain;
-use std::sync::mpsc::{channel, RecvError};
-use threadpool::ThreadPool;
-use num::complex::Complex;
-use image::{ImageBuffer, Pixel, Rgb};
-
-#
-# error_chain! {
-#     foreign_links {
-#         MpscRecv(RecvError);
-#         Io(std::io::Error);
-#     }
-# }
-#
-# // Function converting intensity values to RGB
-# // Based on http://www.efg2.com/Lab/ScienceAndEngineering/Spectra.htm
-# fn wavelength_to_rgb(wavelength: u32) -> Rgb<u8> {
-#     let wave = wavelength as f32;
-#
-#     let (r, g, b) = match wavelength {
-#         380..=439 => ((440. - wave) / (440. - 380.), 0.0, 1.0),
-#         440..=489 => (0.0, (wave - 440.) / (490. - 440.), 1.0),
-#         490..=509 => (0.0, 1.0, (510. - wave) / (510. - 490.)),
-#         510..=579 => ((wave - 510.) / (580. - 510.), 1.0, 0.0),
-#         580..=644 => (1.0, (645. - wave) / (645. - 580.), 0.0),
-#         645..=780 => (1.0, 0.0, 0.0),
-#         _ => (0.0, 0.0, 0.0),
-#     };
-#
-#     let factor = match wavelength {
-#         380..=419 => 0.3 + 0.7 * (wave - 380.) / (420. - 380.),
-#         701..=780 => 0.3 + 0.7 * (780. - wave) / (780. - 700.),
-#         _ => 1.0,
-#     };
-#
-#     let (r, g, b) = (normalize(r, factor), normalize(g, factor), normalize(b, factor));
-#     Rgb::from_channels(r, g, b, 0)
-# }
-#
-# // Maps Julia set distance estimation to intensity values
-# fn julia(c: Complex<f32>, x: u32, y: u32, width: u32, height: u32, max_iter: u32) -> u32 {
-#     let width = width as f32;
-#     let height = height as f32;
-#
-#     let mut z = Complex {
-#         // scale and translate the point to image coordinates
-#         re: 3.0 * (x as f32 - 0.5 * width) / width,
-#         im: 2.0 * (y as f32 - 0.5 * height) / height,
-#     };
-#
-#     let mut i = 0;
-#     for t in 0..max_iter {
-#         if z.norm() >= 2.0 {
-#             break;
-#         }
-#         z = z * z + c;
-#         i = t;
-#     }
-#     i
-# }
-#
-# // Normalizes color intensity values within RGB range
-# fn normalize(color: f32, factor: f32) -> u8 {
-#     ((color * factor).powf(0.8) * 255.) as u8
-# }
-
-fn main() -> Result<()> {
-    let (width, height) = (1920, 1080);
-    // 为指定宽高的输出图片分配内存
-    let mut img = ImageBuffer::new(width, height);
-    let iterations = 300;
-
-    let c = Complex::new(-0.8, 0.156);
-
-    let pool = ThreadPool::new(num_cpus::get());
-    let (tx, rx) = channel();
-
-    for y in 0..height {
-        let tx = tx.clone();
-        // execute 将每个像素作为单独的作业接收
-        pool.execute(move || for x in 0..width {
-                         let i = julia(c, x, y, width, height, iterations);
-                         let pixel = wavelength_to_rgb(380 + i * 400 / iterations);
-                         tx.send((x, y, pixel)).expect("Could not send data!");
-                     });
-    }
-
-    for _ in 0..(width * height) {
-        let (x, y, pixel) = rx.recv()?;
-        // 使用数据来设置像素的颜色
-        img.put_pixel(x, y, pixel);
-    }
-    
-    // 输出图片内容到指定文件中
-    let _ = img.save("output.png")?;
-    Ok(())
-}
-```
\ No newline at end of file
diff --git a/src/cookbook/compression/tar.md b/src/cookbook/compression/tar.md
deleted file mode 100644
index 992d8ddc..00000000
--- a/src/cookbook/compression/tar.md
+++ /dev/null
@@ -1,77 +0,0 @@
-# 使用tar包
-
-## 解压 tar 包
-以下代码将解压缩( [GzDecoder](https://docs.rs/flate2/*/flate2/read/struct.GzDecoder.html) )当前目录中的 `archive.tar.gz` ,并将所有文件抽取出( [Archive::unpack](https://docs.rs/tar/*/tar/struct.Archive.html#method.unpack) )来后当入到当前目录中。
-
-```rust,editable
-use std::fs::File;
-use flate2::read::GzDecoder;
-use tar::Archive;
-
-fn main() -> Result<(), std::io::Error> {
-    let path = "archive.tar.gz";
-
-    let tar_gz = File::open(path)?;
-    let tar = GzDecoder::new(tar_gz);
-    let mut archive = Archive::new(tar);
-    archive.unpack(".")?;
-
-    Ok(())
-}
-```
-
-## 将目录压缩成 tar 包
-以下代码将 `/var/log` 目录压缩成 `archive.tar.gz`:
-
-- 创建一个 [File](https://doc.rust-lang.org/std/fs/struct.File.html) 文件,并使用 [GzEncoder](https://docs.rs/flate2/*/flate2/write/struct.GzEncoder.html) 和 [tar::Builder](https://docs.rs/tar/*/tar/struct.Builder.html) 对其进行包裹
-- 通过 [Builder::append_dir_all](https://docs.rs/tar/*/tar/struct.Builder.html#method.append_dir_all) 将 `/var/log` 目录下的所有内容添加到压缩文件中,该文件在 `backup/logs` 目录下。
-- [GzEncoder](https://docs.rs/flate2/*/flate2/write/struct.GzEncoder.html) 负责在写入压缩文件 `archive.tar.gz` 之前对数据进行压缩。
-
-```rust,editable
-use std::fs::File;
-use flate2::Compression;
-use flate2::write::GzEncoder;
-
-fn main() -> Result<(), std::io::Error> {
-    let tar_gz = File::create("archive.tar.gz")?;
-    let enc = GzEncoder::new(tar_gz, Compression::default());
-    let mut tar = tar::Builder::new(enc);
-    tar.append_dir_all("backup/logs", "/var/log")?;
-    Ok(())
-}
-```
-
-## 解压的同时删除指定的文件前缀
-遍历目录中的文件 [Archive::entries](https://docs.rs/tar/*/tar/struct.Archive.html#method.entries),若解压前的文件名包含 `bundle/logs` 前缀,需要将前缀从文件名移除( [Path::strip_prefix](https://doc.rust-lang.org/std/path/struct.Path.html#method.strip_prefix) )后,再解压。
-
-
-
-```rust,editable
-use std::fs::File;
-use std::path::PathBuf;
-use flate2::read::GzDecoder;
-use tar::Archive;
-
-fn main() -> Result<()> {
-    let file = File::open("archive.tar.gz")?;
-    let mut archive = Archive::new(GzDecoder::new(file));
-    let prefix = "bundle/logs";
-
-    println!("Extracted the following files:");
-    archive
-        .entries()? // 获取压缩档案中的文件条目列表
-        .filter_map(|e| e.ok())
-        // 对每个文件条目进行 map 处理
-        .map(|mut entry| -> Result<PathBuf> {
-            // 将文件路径名中的前缀移除,获取一个新的路径名
-            let path = entry.path()?.strip_prefix(prefix)?.to_owned();
-            // 将内容解压到新的路径名中
-            entry.unpack(&path)?;
-            Ok(path)
-        })
-        .filter_map(|e| e.ok())
-        .for_each(|x| println!("> {}", x.display()));
-
-    Ok(())
-}
-```
\ No newline at end of file
diff --git a/src/cookbook/config.md b/src/cookbook/config.md
deleted file mode 100644
index 33eb3d73..00000000
--- a/src/cookbook/config.md
+++ /dev/null
@@ -1 +0,0 @@
-# 配置文件 todo
diff --git a/src/cookbook/crypto.md b/src/cookbook/crypto.md
deleted file mode 100644
index f9455099..00000000
--- a/src/cookbook/crypto.md
+++ /dev/null
@@ -1 +0,0 @@
-# 加密解密 todo
diff --git a/src/cookbook/cryptography/encryption.md b/src/cookbook/cryptography/encryption.md
deleted file mode 100644
index becab266..00000000
--- a/src/cookbook/cryptography/encryption.md
+++ /dev/null
@@ -1,57 +0,0 @@
-# 加密
-
-### 使用  PBKDF2 对密码进行哈希和加盐( salt )
-[ring::pbkdf2](https://briansmith.org/rustdoc/ring/pbkdf2/index.html) 可以对一个加盐密码进行哈希。
-
-```rust,editable
-
-use data_encoding::HEXUPPER;
-use ring::error::Unspecified;
-use ring::rand::SecureRandom;
-use ring::{digest, pbkdf2, rand};
-use std::num::NonZeroU32;
-
-fn main() -> Result<(), Unspecified> {
-    const CREDENTIAL_LEN: usize = digest::SHA512_OUTPUT_LEN;
-    let n_iter = NonZeroU32::new(100_000).unwrap();
-    let rng = rand::SystemRandom::new();
-
-    let mut salt = [0u8; CREDENTIAL_LEN];
-    // 生成 salt: 将安全生成的随机数填入到字节数组中
-    rng.fill(&mut salt)?;
-
-    let password = "Guess Me If You Can!";
-    let mut pbkdf2_hash = [0u8; CREDENTIAL_LEN];
-    pbkdf2::derive(
-        pbkdf2::PBKDF2_HMAC_SHA512,
-        n_iter,
-        &salt,
-        password.as_bytes(),
-        &mut pbkdf2_hash,
-    );
-    println!("Salt: {}", HEXUPPER.encode(&salt));
-    println!("PBKDF2 hash: {}", HEXUPPER.encode(&pbkdf2_hash));
-
-    // `verify` 检查哈希是否正确
-    let should_`succeed = pbkdf2::verify(
-        pbkdf2::PBKDF2_HMAC_SHA512,
-        n_iter,
-        &salt,
-        password.as_bytes(),
-        &pbkdf2_hash,
-    );
-    let wrong_password = "Definitely not the correct password";
-    let should_fail = pbkdf2::verify(
-        pbkdf2::PBKDF2_HMAC_SHA512,
-        n_iter,
-        &salt,
-        wrong_password.as_bytes(),
-        &pbkdf2_hash,
-    );
-
-    assert!(should_succeed.is_ok());
-    assert!(!should_fail.is_ok());
-
-    Ok(())
-}
-```
\ No newline at end of file
diff --git a/src/cookbook/cryptography/hashing.md b/src/cookbook/cryptography/hashing.md
deleted file mode 100644
index 60a697c0..00000000
--- a/src/cookbook/cryptography/hashing.md
+++ /dev/null
@@ -1,71 +0,0 @@
-# 哈希
-
-### 计算文件的 SHA-256 摘要
-写入一些数据到文件中,然后使用 [digest::Context](https://briansmith.org/rustdoc/ring/digest/struct.Context.html) 来计算文件内容的 SHA-256 摘要 [digest::Digest](https://briansmith.org/rustdoc/ring/digest/struct.Digest.html)。
-
-```rust,editable
-# use error_chain::error_chain;
-use data_encoding::HEXUPPER;
-use ring::digest::{Context, Digest, SHA256};
-use std::fs::File;
-use std::io::{BufReader, Read, Write};
-
-# error_chain! {
-#    foreign_links {
-#        Io(std::io::Error);
-#        Decode(data_encoding::DecodeError);
-#    }
-# }
-
-fn sha256_digest<R: Read>(mut reader: R) -> Result<Digest> {
-    let mut context = Context::new(&SHA256);
-    let mut buffer = [0; 1024];
-
-    loop {
-        let count = reader.read(&mut buffer)?;
-        if count == 0 {
-            break;
-        }
-        context.update(&buffer[..count]);
-    }
-
-    Ok(context.finish())
-}
-
-fn main() -> Result<()> {
-    let path = "file.txt";
-
-    let mut output = File::create(path)?;
-    write!(output, "We will generate a digest of this text")?;
-
-    let input = File::open(path)?;
-    let reader = BufReader::new(input);
-    let digest = sha256_digest(reader)?;
-
-    println!("SHA-256 digest is {}", HEXUPPER.encode(digest.as_ref()));
-
-    Ok(())
-}
-```
-
-### 使用 HMAC 摘要来签名和验证消息
-使用 [ring::hmac](https://briansmith.org/rustdoc/ring/hmac/) 创建一个字符串签名并检查该签名的正确性。
-
-```rust,editable
-use ring::{hmac, rand};
-use ring::rand::SecureRandom;
-use ring::error::Unspecified;
-
-fn main() -> Result<(), Unspecified> {
-    let mut key_value = [0u8; 48];
-    let rng = rand::SystemRandom::new();
-    rng.fill(&mut key_value)?;
-    let key = hmac::Key::new(hmac::HMAC_SHA256, &key_value);
-
-    let message = "Legitimate and important message.";
-    let signature = hmac::sign(&key, message.as_bytes());
-    hmac::verify(&key, message.as_bytes(), signature.as_ref())?;
-
-    Ok(())
-}
-```
\ No newline at end of file
diff --git a/src/cookbook/database.md b/src/cookbook/database.md
deleted file mode 100644
index de785679..00000000
--- a/src/cookbook/database.md
+++ /dev/null
@@ -1 +0,0 @@
-# 数据库访问 todo
diff --git a/src/cookbook/database/postgres.md b/src/cookbook/database/postgres.md
deleted file mode 100644
index 13bd37dd..00000000
--- a/src/cookbook/database/postgres.md
+++ /dev/null
@@ -1,123 +0,0 @@
-# Postgres
-
-### 在数据库中创建表格
-我们通过 [postgres](https://docs.rs/postgres/0.17.2/postgres/) 来操作数据库。下面的例子有一个前提:数据库 `library` 已经存在,其中用户名和密码都是 `postgres`。
-
-```rust,editable
-use postgres::{Client, NoTls, Error};
-
-fn main() -> Result<(), Error> {
-    // 连接到数据库 library
-    let mut client = Client::connect("postgresql://postgres:postgres@localhost/library", NoTls)?;
-    
-    client.batch_execute("
-        CREATE TABLE IF NOT EXISTS author (
-            id              SERIAL PRIMARY KEY,
-            name            VARCHAR NOT NULL,
-            country         VARCHAR NOT NULL
-            )
-    ")?;
-
-    client.batch_execute("
-        CREATE TABLE IF NOT EXISTS book  (
-            id              SERIAL PRIMARY KEY,
-            title           VARCHAR NOT NULL,
-            author_id       INTEGER NOT NULL REFERENCES author
-            )
-    ")?;
-
-    Ok(())
-
-}
-```
-
-### 插入和查询
-
-```rust,editable
-use postgres::{Client, NoTls, Error};
-use std::collections::HashMap;
-
-struct Author {
-    _id: i32,
-    name: String,
-    country: String
-}
-
-fn main() -> Result<(), Error> {
-    let mut client = Client::connect("postgresql://postgres:postgres@localhost/library", 
-                                    NoTls)?;
-    
-    let mut authors = HashMap::new();
-    authors.insert(String::from("Chinua Achebe"), "Nigeria");
-    authors.insert(String::from("Rabindranath Tagore"), "India");
-    authors.insert(String::from("Anita Nair"), "India");
-
-    for (key, value) in &authors {
-        let author = Author {
-            _id: 0,
-            name: key.to_string(),
-            country: value.to_string()
-        };
-
-        // 插入数据
-        client.execute(
-                "INSERT INTO author (name, country) VALUES ($1, $2)",
-                &[&author.name, &author.country],
-        )?;
-    }
-
-    // 查询数据
-    for row in client.query("SELECT id, name, country FROM author", &[])? {
-        let author = Author {
-            _id: row.get(0),
-            name: row.get(1),
-            country: row.get(2),
-        };
-        println!("Author {} is from {}", author.name, author.country);
-    }
-
-    Ok(())
-
-}
-```
-
-### 聚合数据
-
-下面代码将使用降序的方式列出 [Museum of Modern Art]() 数据库中的前 7999 名艺术家的国籍分布.
-
-```rust,editable
-use postgres::{Client, Error, NoTls};
-
-struct Nation {
-    nationality: String,
-    count: i64,
-}
-
-fn main() -> Result<(), Error> {
-    let mut client = Client::connect(
-        "postgresql://postgres:postgres@127.0.0.1/moma",
-        NoTls,
-    )?;
-
-    for row in client.query 
-    ("SELECT nationality, COUNT(nationality) AS count 
-    FROM artists GROUP BY nationality ORDER BY count DESC", &[])? {
-        
-        let (nationality, count) : (Option<String>, Option<i64>) 
-        = (row.get (0), row.get (1));
-        
-        if nationality.is_some () && count.is_some () {
-
-            let nation = Nation{
-                nationality: nationality.unwrap(),
-                count: count.unwrap(),
-        };
-            println!("{} {}", nation.nationality, nation.count);
-            
-        }
-    }
-
-    Ok(())
-}
-```
-
diff --git a/src/cookbook/database/sqlite.md b/src/cookbook/database/sqlite.md
deleted file mode 100644
index 0872fbf2..00000000
--- a/src/cookbook/database/sqlite.md
+++ /dev/null
@@ -1,136 +0,0 @@
-# SQLite
-
-### 创建 SQLite 数据库
-
-使用 `rusqlite` 可以创建 SQLite 数据库,[Connection::open](https://docs.rs/rusqlite/*/rusqlite/struct.Connection.html#method.open) 会尝试打开一个数据库,若不存在,则创建新的数据库。
-
-> 这里创建的 `cats.db` 数据库将被后面的例子所使用
-
-
-```rust,editable
-use rusqlite::{Connection, Result};
-use rusqlite::NO_PARAMS;
-
-fn main() -> Result<()> {
-    let conn = Connection::open("cats.db")?;
-
-    conn.execute(
-        "create table if not exists cat_colors (
-             id integer primary key,
-             name text not null unique
-         )",
-        NO_PARAMS,
-    )?;
-    conn.execute(
-        "create table if not exists cats (
-             id integer primary key,
-             name text not null,
-             color_id integer not null references cat_colors(id)
-         )",
-        NO_PARAMS,
-    )?;
-
-    Ok(())
-}
-```
-
-### 插入和查询
-
-```rust,editable
-
-use rusqlite::NO_PARAMS;
-use rusqlite::{Connection, Result};
-use std::collections::HashMap;
-
-#[derive(Debug)]
-struct Cat {
-    name: String,
-    color: String,
-}
-
-fn main() -> Result<()> {
-    // 打开第一个例子所创建的数据库
-    let conn = Connection::open("cats.db")?;
-
-    let mut cat_colors = HashMap::new();
-    cat_colors.insert(String::from("Blue"), vec!["Tigger", "Sammy"]);
-    cat_colors.insert(String::from("Black"), vec!["Oreo", "Biscuit"]);
-
-    for (color, catnames) in &cat_colors {
-        // 插入一条数据行
-        conn.execute(
-            "INSERT INTO cat_colors (name) values (?1)",
-            &[&color.to_string()],
-        )?;
-        // 获取最近插入数据行的 id
-        let last_id: String = conn.last_insert_rowid().to_string();
-
-        for cat in catnames {
-            conn.execute(
-                "INSERT INTO cats (name, color_id) values (?1, ?2)",
-                &[&cat.to_string(), &last_id],
-            )?;
-        }
-    }
-    let mut stmt = conn.prepare(
-        "SELECT c.name, cc.name from cats c
-         INNER JOIN cat_colors cc
-         ON cc.id = c.color_id;",
-    )?;
-
-    let cats = stmt.query_map(NO_PARAMS, |row| {
-        Ok(Cat {
-            name: row.get(0)?,
-            color: row.get(1)?,
-        })
-    })?;
-
-    for cat in cats {
-        println!("Found cat {:?}", cat);
-    }
-
-    Ok(())
-}
-```
-
-### 使用事务
-使用 [Connection::transaction](https://docs.rs/rusqlite/*/rusqlite/struct.Connection.html#method.transaction) 可以开始新的事务,若没有对事务进行显式地提交 [Transaction::commit](https://docs.rs/rusqlite/0.27.0/rusqlite/struct.Transaction.html#method.commit),则会进行回滚。
-
-下面的例子中,`rolled_back_tx` 插入了重复的颜色名称,会发生回滚。
-
-```rust,editable
-use rusqlite::{Connection, Result, NO_PARAMS};
-
-fn main() -> Result<()> {
-    // 打开第一个例子所创建的数据库
-    let mut conn = Connection::open("cats.db")?;
-
-    successful_tx(&mut conn)?;
-
-    let res = rolled_back_tx(&mut conn);
-    assert!(res.is_err());
-
-    Ok(())
-}
-
-fn successful_tx(conn: &mut Connection) -> Result<()> {
-    let tx = conn.transaction()?;
-
-    tx.execute("delete from cat_colors", NO_PARAMS)?;
-    tx.execute("insert into cat_colors (name) values (?1)", &[&"lavender"])?;
-    tx.execute("insert into cat_colors (name) values (?1)", &[&"blue"])?;
-
-    tx.commit()
-}
-
-fn rolled_back_tx(conn: &mut Connection) -> Result<()> {
-    let tx = conn.transaction()?;
-
-    tx.execute("delete from cat_colors", NO_PARAMS)?;
-    tx.execute("insert into cat_colors (name) values (?1)", &[&"lavender"])?;
-    tx.execute("insert into cat_colors (name) values (?1)", &[&"blue"])?;
-    tx.execute("insert into cat_colors (name) values (?1)", &[&"lavender"])?;
-
-    tx.commit()
-}
-```
\ No newline at end of file
diff --git a/src/cookbook/datastructures/bitfield.md b/src/cookbook/datastructures/bitfield.md
deleted file mode 100644
index 4c9c29e0..00000000
--- a/src/cookbook/datastructures/bitfield.md
+++ /dev/null
@@ -1,47 +0,0 @@
-# 位字段
-
-### 定义和操作位字段
-使用 [`bitflags!`](https://docs.rs/bitflags/1.3.2/bitflags/macro.bitflags.html) 宏可以帮助我们创建安全的位字段类型 `MyFlags`,然后为其实现基本的 `clear` 操作。以下代码展示了基本的位操作和格式化:
-```rust,editable
-use bitflags::bitflags;
-use std::fmt;
-
-bitflags! {
-    struct MyFlags: u32 {
-        const FLAG_A       = 0b00000001;
-        const FLAG_B       = 0b00000010;
-        const FLAG_C       = 0b00000100;
-        const FLAG_ABC     = Self::FLAG_A.bits
-                           | Self::FLAG_B.bits
-                           | Self::FLAG_C.bits;
-    }
-}
-
-impl MyFlags {
-    pub fn clear(&mut self) -> &mut MyFlags {
-        self.bits = 0;  
-        self
-    }
-}
-
-impl fmt::Display for MyFlags {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{:032b}", self.bits)
-    }
-}
-
-fn main() {
-    let e1 = MyFlags::FLAG_A | MyFlags::FLAG_C;
-    let e2 = MyFlags::FLAG_B | MyFlags::FLAG_C;
-    assert_eq!((e1 | e2), MyFlags::FLAG_ABC);   
-    assert_eq!((e1 & e2), MyFlags::FLAG_C);    
-    assert_eq!((e1 - e2), MyFlags::FLAG_A);    
-    assert_eq!(!e2, MyFlags::FLAG_A);           
-
-    let mut flags = MyFlags::FLAG_ABC;
-    assert_eq!(format!("{}", flags), "00000000000000000000000000000111");
-    assert_eq!(format!("{}", flags.clear()), "00000000000000000000000000000000");
-    assert_eq!(format!("{:?}", MyFlags::FLAG_B), "FLAG_B");
-    assert_eq!(format!("{:?}", MyFlags::FLAG_A | MyFlags::FLAG_B), "FLAG_A | FLAG_B");
-}
-```
\ No newline at end of file
diff --git a/src/cookbook/date.md b/src/cookbook/date.md
deleted file mode 100644
index a434528a..00000000
--- a/src/cookbook/date.md
+++ /dev/null
@@ -1 +0,0 @@
-# 时间日期
diff --git a/src/cookbook/datetime/duration.md b/src/cookbook/datetime/duration.md
deleted file mode 100644
index 4f14bff9..00000000
--- a/src/cookbook/datetime/duration.md
+++ /dev/null
@@ -1,69 +0,0 @@
-# 时间计算和转换
-
-### 测量某段代码的耗时
-测量从 [time::Instant::now](https://doc.rust-lang.org/std/time/struct.Instant.html#method.now) 开始所经过的时间 [time::Instant::elapsed](https://doc.rust-lang.org/std/time/struct.Instant.html#method.elapsed).
-
-```rust,editable
-use std::time::{Duration, Instant};
-
-fn main() {
-    let start = Instant::now();
-    expensive_function();
-    let duration = start.elapsed();
-
-    println!("Time elapsed in expensive_function() is: {:?}", duration);
-}
-```
-
-### 对日期和时间进行计算
-使用 [DateTime::checked_add_signed](https://docs.rs/chrono/*/chrono/struct.Date.html#method.checked_add_signed) 计算和显示从现在开始两周后的日期和时间,然后再计算一天前的日期 [DateTime::checked_sub_signed](https://docs.rs/chrono/*/chrono/struct.Date.html#method.checked_sub_signed)。
-
-[DateTime::format](https://docs.rs/chrono/*/chrono/struct.DateTime.html#method.format) 所支持的转义序列可以在 [chrono::format::strftime](https://docs.rs/chrono/*/chrono/format/strftime/index.html) 找到.
-
-```rust,editable
-use chrono::{DateTime, Duration, Utc};
-
-fn day_earlier(date_time: DateTime<Utc>) -> Option<DateTime<Utc>> {
-    date_time.checked_sub_signed(Duration::days(1))
-}
-
-fn main() {
-    let now = Utc::now();
-    println!("{}", now);
-
-    let almost_three_weeks_from_now = now.checked_add_signed(Duration::weeks(2))
-            .and_then(|in_2weeks| in_2weeks.checked_add_signed(Duration::weeks(1)))
-            .and_then(day_earlier);
-
-    match almost_three_weeks_from_now {
-        Some(x) => println!("{}", x),
-        None => eprintln!("Almost three weeks from now overflows!"),
-    }
-
-    match now.checked_add_signed(Duration::max_value()) {
-        Some(x) => println!("{}", x),
-        None => eprintln!("We can't use chrono to tell the time for the Solar System to complete more than one full orbit around the galactic center."),
-    }
-}
-```
-
-### 将本地时间转换成其它时区
-使用 [offset::Local::now](https://docs.rs/chrono/*/chrono/offset/struct.Local.html#method.now) 获取本地时间并进行显示,接着,使用 [DateTime::from_utc](https://docs.rs/chrono/*/chrono/struct.DateTime.html#method.from_utc) 将它转换成 UTC 标准时间。最后,再使用 [offset::FixedOffset](https://docs.rs/chrono/*/chrono/offset/struct.FixedOffset.html) 将 UTC 时间转换成 UTC+8 和 UTC-2 的时间。
-
-```rust,editable
-use chrono::{DateTime, FixedOffset, Local, Utc};
-
-fn main() {
-    let local_time = Local::now();
-    let utc_time = DateTime::<Utc>::from_utc(local_time.naive_utc(), Utc);
-    let china_timezone = FixedOffset::east(8 * 3600);
-    let rio_timezone = FixedOffset::west(2 * 3600);
-    println!("Local time now is {}", local_time);
-    println!("UTC time now is {}", utc_time);
-    println!(
-        "Time in Hong Kong now is {}",
-        utc_time.with_timezone(&china_timezone)
-    );
-    println!("Time in Rio de Janeiro now is {}", utc_time.with_timezone(&rio_timezone));
-}
-```
\ No newline at end of file
diff --git a/src/cookbook/datetime/parsing.md b/src/cookbook/datetime/parsing.md
deleted file mode 100644
index aee55576..00000000
--- a/src/cookbook/datetime/parsing.md
+++ /dev/null
@@ -1,114 +0,0 @@
-# 解析和显示
-
-### 检查日期和时间
-通过 [DateTime](https://docs.rs/chrono/*/chrono/struct.DateTime.html) 获取当前的 UTC 时间:
-- [Timelike](https://docs.rs/chrono/*/chrono/trait.Timelike.html), 时/分/秒
-- [Datelike](https://docs.rs/chrono/*/chrono/trait.Datelike.html), 年/月/日
-
-```rust,editable
-use chrono::{Datelike, Timelike, Utc};
-
-fn main() {
-    let now = Utc::now();
-
-    let (is_pm, hour) = now.hour12();
-    println!(
-        "The current UTC time is {:02}:{:02}:{:02} {}",
-        hour,
-        now.minute(),
-        now.second(),
-        if is_pm { "PM" } else { "AM" }
-    );
-    println!(
-        "And there have been {} seconds since midnight",
-        now.num_seconds_from_midnight()
-    );
-
-    let (is_common_era, year) = now.year_ce();
-    println!(
-        "The current UTC date is {}-{:02}-{:02} {:?} ({})",
-        year,
-        now.month(),
-        now.day(),
-        now.weekday(),
-        if is_common_era { "CE" } else { "BCE" }
-    );
-    println!(
-        "And the Common Era began {} days ago",
-        now.num_days_from_ce()
-    );
-}
-```
-
-### 日期和时间戳的相互转换
-
-```rust,editable
-use chrono::{NaiveDate, NaiveDateTime};
-
-fn main() {
-    // 生成一个具体的日期时间
-    let date_time: NaiveDateTime = NaiveDate::from_ymd(2017, 11, 12).and_hms(17, 33, 44);
-    println!(
-        "Number of seconds between 1970-01-01 00:00:00 and {} is {}.",
-        // 打印日期和日期对应的时间戳
-        date_time, date_time.timestamp());
-
-    // 计算从 1970 1月1日 0:00:00 UTC 开始,10亿秒后是什么日期时间
-    let date_time_after_a_billion_seconds = NaiveDateTime::from_timestamp(1_000_000_000, 0);
-    println!(
-        "Date after a billion seconds since 1970-01-01 00:00:00 was {}.",
-        date_time_after_a_billion_seconds);
-}
-```
-
-### 显示格式化的日期和时间
-通过 [Utc::now](https://docs.rs/chrono/*/chrono/offset/struct.Utc.html#method.now) 可以获取当前的 UTC 时间。
-
-```rust,editable
-use chrono::{DateTime, Utc};
-
-fn main() {
-    let now: DateTime<Utc> = Utc::now();
-
-    println!("UTC now is: {}", now);
-    // 使用 RFC 2822 格式显示当前时间
-    println!("UTC now in RFC 2822 is: {}", now.to_rfc2822());
-    // 使用 RFC 3339 格式显示当前时间
-    println!("UTC now in RFC 3339 is: {}", now.to_rfc3339());
-    // 使用自定义格式显示当前时间
-    println!("UTC now in a custom format is: {}", now.format("%a %b %e %T %Y"));
-}
-```
-
-### 将字符串解析为 DateTime 结构体
-我们可以将多种格式的日期时间字符串转换成 [DateTime](https://docs.rs/chrono/*/chrono/struct.DateTime.html) 结构体。[DateTime::parse_from_str](https://docs.rs/chrono/*/chrono/struct.DateTime.html#method.parse_from_str) 使用的转义序列可以在 [chrono::format::strftime](https://docs.rs/chrono/0.4.19/chrono/format/strftime/index.html) 找到.
-
-只有当能唯一的标识出日期和时间时,才能创建 `DateTime`。如果要在没有时区的情况下解析日期或时间,你需要使用 [`NativeDate`](https://docs.rs/chrono/*/chrono/naive/struct.NaiveDate.html) 等函数。
-
-```rust,editable
-use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime};
-use chrono::format::ParseError;
-
-
-fn main() -> Result<(), ParseError> {
-    let rfc2822 = DateTime::parse_from_rfc2822("Tue, 1 Jul 2003 10:52:37 +0200")?;
-    println!("{}", rfc2822);
-
-    let rfc3339 = DateTime::parse_from_rfc3339("1996-12-19T16:39:57-08:00")?;
-    println!("{}", rfc3339);
-    
-    let custom = DateTime::parse_from_str("5.8.1994 8:00 am +0000", "%d.%m.%Y %H:%M %P %z")?;
-    println!("{}", custom);
-
-    let time_only = NaiveTime::parse_from_str("23:56:04", "%H:%M:%S")?;
-    println!("{}", time_only);
-
-    let date_only = NaiveDate::parse_from_str("2015-09-05", "%Y-%m-%d")?;
-    println!("{}", date_only);
-
-    let no_timezone = NaiveDateTime::parse_from_str("2015-09-05 23:56:04", "%Y-%m-%d %H:%M:%S")?;
-    println!("{}", no_timezone);
-
-    Ok(())
-}
-```
\ No newline at end of file
diff --git a/src/cookbook/dev/intro.md b/src/cookbook/dev/intro.md
deleted file mode 100644
index f0cc50f0..00000000
--- a/src/cookbook/dev/intro.md
+++ /dev/null
@@ -1 +0,0 @@
-# 开发调试
diff --git a/src/cookbook/dev/logs.md b/src/cookbook/dev/logs.md
deleted file mode 100644
index 65c2dbdd..00000000
--- a/src/cookbook/dev/logs.md
+++ /dev/null
@@ -1 +0,0 @@
-# 日志
diff --git a/src/cookbook/dev/profile.md b/src/cookbook/dev/profile.md
deleted file mode 100644
index 124678e8..00000000
--- a/src/cookbook/dev/profile.md
+++ /dev/null
@@ -1 +0,0 @@
-# 性能分析
diff --git a/src/cookbook/devtools/build-tools.md b/src/cookbook/devtools/build-tools.md
deleted file mode 100644
index b3bdc6ad..00000000
--- a/src/cookbook/devtools/build-tools.md
+++ /dev/null
@@ -1,192 +0,0 @@
-# 构建时工具
-本章节的内容是关于构建工具的,如果大家没有听说过 `build.rs` 文件,强烈建议先看看[这里](https://course.rs/cargo/reference/build-script/intro.html)了解下何为构建工具。
-
-### 编译并静态链接一个 C 库
-
-[cc](https://docs.rs/cc/latest/cc/) 包能帮助我们更好地跟 C/C++/汇编进行交互:它提供了简单的 API 可以将外部的库编译成静态库( .a ),然后通过 `rustc` 进行静态链接。
-
-下面的例子中,我们将在 Rust 代码中使用 C 的代码: *src/hello.c*。在开始编译 Rust 的项目代码前,`build.rs` 构建脚本将先被执行。通过 cc 包,一个静态的库可以被生成( *libhello.a* ),然后该库将被 Rust的代码所使用:通过 `extern` 声明外部函数签名的方式来使用。
-
-由于例子中的 C 代码很简单,因此只需要将一个文件传递给 [cc::Build](https://docs.rs/cc/*/cc/struct.Build.html)。如果大家需要更复杂的构建,`cc::Build` 还提供了通过 [include](https://docs.rs/cc/*/cc/struct.Build.html#method.include) 来包含路径的方式,以及额外的编译标志( [flags](https://docs.rs/cc/1.0.73/cc/struct.Build.html#method.flag) )。
-
-*Cargo.toml*
-
-```toml
-[package]
-...
-build = "build.rs"
-
-[build-dependencies]
-cc = "1"
-
-[dependencies]
-error-chain = "0.11"
-```
-
-*build.rs*
-
-```rust
-fn main() {
-    cc::Build::new()
-        .file("src/hello.c")
-        .compile("hello");   // outputs `libhello.a`
-}
-```
-
-*src/hello.c*
-
-```C
-#include <stdio.h>
-
-
-void hello() {
-    printf("Hello from C!\n");
-}
-
-void greet(const char* name) {
-    printf("Hello, %s!\n", name);
-}
-```
-
-*src/main.rs*
-
-```rust
-use error_chain::error_chain;
-use std::ffi::CString;
-use std::os::raw::c_char;
-
-error_chain! {
-    foreign_links {
-        NulError(::std::ffi::NulError);
-        Io(::std::io::Error);
-    }
-}
-fn prompt(s: &str) -> Result<String> {
-    use std::io::Write;
-    print!("{}", s);
-    std::io::stdout().flush()?;
-    let mut input = String::new();
-    std::io::stdin().read_line(&mut input)?;
-    Ok(input.trim().to_string())
-}
-
-extern {
-    fn hello();
-    fn greet(name: *const c_char);
-}
-
-fn main() -> Result<()> {
-    unsafe { hello() }
-    let name = prompt("What's your name? ")?;
-    let c_name = CString::new(name)?;
-    unsafe { greet(c_name.as_ptr()) }
-    Ok(())
-}
-```
-
-###  编译并静态链接一个 C++ 库
-链接到 C++ 库跟之前的方式非常相似。主要的区别在于链接到 C++ 库时,你需要通过构建方法 [cpp(true)](https://docs.rs/cc/*/cc/struct.Build.html#method.cpp) 来指定一个 C++ 编译器,然后在 C++ 的代码顶部添加 `extern "C"` 来阻止 C++ 编译器对库名进行名称重整( name mangling )。
-
-*Cargo.toml*
-
-```toml
-[package]
-...
-build = "build.rs"
-
-[build-dependencies]
-cc = "1"
-```
-
-*build.rs*
-
-```rust
-fn main() {
-    cc::Build::new()
-        .cpp(true)
-        .file("src/foo.cpp")
-        .compile("foo");   
-}
-```
-
-*src/foo.cpp*
-
-```c++
-extern "C" {
-    int multiply(int x, int y);
-}
-
-int multiply(int x, int y) {
-    return x*y;
-}
-```
-
-*src/main.rs*
-
-```rust
-extern {
-    fn multiply(x : i32, y : i32) -> i32;
-}
-
-fn main(){
-    unsafe {
-        println!("{}", multiply(5,7));
-    }   
-}
-```
-
-### 为 C 库创建自定义的 define
-
-[cc::Build::define](https://docs.rs/cc/*/cc/struct.Build.html#method.define) 可以让我们使用自定义的 define 来构建 C 库。
-
-以下示例在构建脚本 `build.rs` 中动态定义了一个 define,然后在运行时打印出 **Welcome to foo - version 1.0.2**。Cargo 会设置一些[环境变量](https://doc.rust-lang.org/cargo/reference/environment-variables.html),它们对于自定义的 define 会有所帮助。
-
-*Cargo.toml*
-
-```toml
-[package]
-...
-version = "1.0.2"
-build = "build.rs"
-
-[build-dependencies]
-cc = "1"
-```
-
-*build.rs*
-```rust
-fn main() {
-    cc::Build::new()
-        .define("APP_NAME", "\"foo\"")
-        .define("VERSION", format!("\"{}\"", env!("CARGO_PKG_VERSION")).as_str())
-        .define("WELCOME", None)
-        .file("src/foo.c")
-        .compile("foo");
-}
-```
-
-*src/foo.c*
-```C
-#include <stdio.h>
-
-void print_app_info() {
-#ifdef WELCOME
-    printf("Welcome to ");
-#endif
-    printf("%s - version %s\n", APP_NAME, VERSION);
-}
-```
-
-*src/main.rs*
-```rust
-extern {
-    fn print_app_info();
-}
-
-fn main(){
-    unsafe {
-        print_app_info();
-    }   
-}
-```
-
diff --git a/src/cookbook/devtools/config-log.md b/src/cookbook/devtools/config-log.md
deleted file mode 100644
index 1121571f..00000000
--- a/src/cookbook/devtools/config-log.md
+++ /dev/null
@@ -1,140 +0,0 @@
-# 配置日志
-
-### 为每个模块开启独立的日志级别
-下面代码创建了模块 `foo` 和嵌套模块 `foo::bar`,并通过 [RUST_LOG](https://docs.rs/env_logger/*/env_logger/#enabling-logging) 环境变量对各自的日志级别进行了控制。
-
-```rust,editable
-mod foo {
-    mod bar {
-        pub fn run() {
-            log::warn!("[bar] warn");
-            log::info!("[bar] info");
-            log::debug!("[bar] debug");
-        }
-    }
-
-    pub fn run() {
-        log::warn!("[foo] warn");
-        log::info!("[foo] info");
-        log::debug!("[foo] debug");
-        bar::run();
-    }
-}
-
-fn main() {
-    env_logger::init();
-    log::warn!("[root] warn");
-    log::info!("[root] info");
-    log::debug!("[root] debug");
-    foo::run();
-}
-```
-
-要让环境变量生效,首先需要通过 `env_logger::init()` 开启相关的支持。然后通过以下命令来运行程序:
-```shell
-RUST_LOG="warn,test::foo=info,test::foo::bar=debug" ./test
-```
-
-此时的默认日志级别被设置为 `warn`,但我们还将 `foo` 模块级别设置为 `info`, `foo::bar` 模块日志级别设置为 `debug`。
-
-```bash
-WARN:test: [root] warn
-WARN:test::foo: [foo] warn
-INFO:test::foo: [foo] info
-WARN:test::foo::bar: [bar] warn
-INFO:test::foo::bar: [bar] info
-DEBUG:test::foo::bar: [bar] debug
-```
-
-### 使用自定义环境变量来设置日志
-
-[Builder](https://docs.rs/env_logger/*/env_logger/struct.Builder.html) 将对日志进行配置,以下代码使用 `MY_APP_LOG` 来替代 `RUST_LOG` 环境变量:
-
-```rust,editable
-use std::env;
-use env_logger::Builder;
-
-fn main() {
-    Builder::new()
-        .parse(&env::var("MY_APP_LOG").unwrap_or_default())
-        .init();
-
-    log::info!("informational message");
-    log::warn!("warning message");
-    log::error!("this is an error {}", "message");
-}
-```
-
-### 在日志中包含时间戳
-
-```rust,editable
-use std::io::Write;
-use chrono::Local;
-use env_logger::Builder;
-use log::LevelFilter;
-
-fn main() {
-    Builder::new()
-        .format(|buf, record| {
-            writeln!(buf,
-                "{} [{}] - {}",
-                Local::now().format("%Y-%m-%dT%H:%M:%S"),
-                record.level(),
-                record.args()
-            )
-        })
-        .filter(None, LevelFilter::Info)
-        .init();
-
-    log::warn!("warn");
-    log::info!("info");
-    log::debug!("debug");
-}
-```
-
-以下是 `stderr` 的输出:
-```shell
-2022-03-22T21:57:06 [WARN] - warn
-2022-03-22T21:57:06 [INFO] - info
-```
-
-### 将日志输出到指定文件
-[log4rs](https://docs.rs/log4rs/) 可以帮我们将日志输出指定的位置,它可以使用外部 YAML 文件或 `builder` 的方式进行配置。
-
-```rust,editable
-# use error_chain::error_chain;
-
-use log::LevelFilter;
-use log4rs::append::file::FileAppender;
-use log4rs::encode::pattern::PatternEncoder;
-use log4rs::config::{Appender, Config, Root};
-
-#error_chain! {
-#    foreign_links {
-#        Io(std::io::Error);
-#        LogConfig(log4rs::config::Errors);
-#        SetLogger(log::SetLoggerError);
-#    }
-#}
-
-fn main() -> Result<()> {
-    // 创建日志配置,并指定输出的位置
-    let logfile = FileAppender::builder()
-        // 编码模式的详情参见: https://docs.rs/log4rs/1.0.0/log4rs/encode/pattern/index.html
-        .encoder(Box::new(PatternEncoder::new("{l} - {m}\n")))
-        .build("log/output.log")?;
-
-    let config = Config::builder()
-        .appender(Appender::builder().build("logfile", Box::new(logfile)))
-        .build(Root::builder()
-                   .appender("logfile")
-                   .build(LevelFilter::Info))?;
-
-    log4rs::init_config(config)?;
-
-    log::info!("Hello, world!");
-
-    Ok(())
-}
-
-```
\ No newline at end of file
diff --git a/src/cookbook/devtools/log.md b/src/cookbook/devtools/log.md
deleted file mode 100644
index adc6616a..00000000
--- a/src/cookbook/devtools/log.md
+++ /dev/null
@@ -1,127 +0,0 @@
-# 日志
-
-## log 包
-[log](https://docs.rs/crate/log/0.4.16) 提供了日志相关的实用工具。
-
-### 在控制台打印 debug 信息
-`env_logger` 通过环境变量来配置日志。[log::debug!](https://docs.rs/log/0.4.16/log/macro.debug.html) 使用起来跟 [std::fmt](https://doc.rust-lang.org/std/fmt/) 中的格式化字符串很像。
-
-```rust
-fn execute_query(query: &str) {
-    log::debug!("Executing query: {}", query);
-}
-
-fn main() {
-    env_logger::init();
-
-    execute_query("DROP TABLE students");
-}
-```
-
-如果大家运行代码,会发现没有任何日志输出,原因是默认的日志级别是 `error`,因此我们需要通过 `RUST_LOG` 环境变量来设置下新的日志级别:
-```shell
-$ RUST_LOG=debug cargo run
-```
-
-然后你将成功看到以下输出:
-```shell
-DEBUG:main: Executing query: DROP TABLE students
-```
-
-### 将错误日志输出到控制台
-下面我们通过 [log::error!](https://docs.rs/log/0.4.16/log/macro.error.html) 将错误日志输出到标准错误 `stderr`。
-
-```rust
-fn execute_query(_query: &str) -> Result<(), &'static str> {
-    Err("I'm afraid I can't do that")
-}
-
-fn main() {
-    env_logger::init();
-
-    let response = execute_query("DROP TABLE students");
-    if let Err(err) = response {
-        log::error!("Failed to execute query: {}", err);
-    }
-}
-```
-
-### 将错误输出到标准输出 stdout
-默认的错误会输出到标准错误输出 `stderr`,下面我们通过自定的配置来让错误输出到标准输出 `stdout`。
-
-```rust,editable
-use env_logger::{Builder, Target};
-
-fn main() {
-    Builder::new()
-        .target(Target::Stdout)
-        .init();
-
-    log::error!("This error has been printed to Stdout");
-}
-```
-
-### 使用自定义 logger
-下面的代码将实现一个自定义 logger `ConsoleLogger`,输出到标准输出 `stdout`。为了使用日志宏,`ConsoleLogger` 需要实现 [log::Log](https://docs.rs/log/*/log/trait.Log.html) 特征,然后使用 [log::set_logger](https://docs.rs/log/*/log/fn.set_logger.html) 来安装使用。
-
-```rust,editable
-use log::{Record, Level, Metadata, LevelFilter, SetLoggerError};
-
-static CONSOLE_LOGGER: ConsoleLogger = ConsoleLogger;
-
-struct ConsoleLogger;
-
-impl log::Log for ConsoleLogger {
-  fn enabled(&self, metadata: &Metadata) -> bool {
-     metadata.level() <= Level::Info
-    }
-
-    fn log(&self, record: &Record) {
-        if self.enabled(record.metadata()) {
-            println!("Rust says: {} - {}", record.level(), record.args());
-        }
-    }
-
-    fn flush(&self) {}
-}
-
-fn main() -> Result<(), SetLoggerError> {
-    log::set_logger(&CONSOLE_LOGGER)?;
-    log::set_max_level(LevelFilter::Info);
-
-    log::info!("hello log");
-    log::warn!("warning");
-    log::error!("oops");
-    Ok(())
-}
-```
-
-### 输出到 Unix syslog
-下面的代码将使用 [syslog](https://docs.rs/crate/syslog/6.0.1) 包将日志输出到 [Unix Syslog](https://www.gnu.org/software/libc/manual/html_node/Overview-of-Syslog.html).
-
-```rust,editable
-#[cfg(target_os = "linux")]
-#[cfg(target_os = "linux")]
-use syslog::{Facility, Error};
-
-#[cfg(target_os = "linux")]
-fn main() -> Result<(), Error> {
-    // 初始化 logger
-    syslog::init(Facility::LOG_USER,
-                 log::LevelFilter::Debug,
-                 // 可选的应用名称
-                 Some("My app name"))?;
-    log::debug!("this is a debug {}", "message");
-    log::error!("this is an error!");
-    Ok(())
-}
-
-#[cfg(not(target_os = "linux"))]
-fn main() {
-    println!("So far, only Linux systems are supported.");
-}
-```
-
-
-## tracing
-@todo
\ No newline at end of file
diff --git a/src/cookbook/devtools/version.md b/src/cookbook/devtools/version.md
deleted file mode 100644
index 2d9ff997..00000000
--- a/src/cookbook/devtools/version.md
+++ /dev/null
@@ -1,186 +0,0 @@
-# 版本号
-
-### 解析并增加版本号
-下面例子使用 [Version::parse](https://docs.rs/semver/*/semver/struct.Version.html#method.parse) 将一个字符串转换成 [semver::Version](https://docs.rs/semver/*/semver/struct.Version.html) 版本号,然后将它的 patch, minor, major 版本号都增加 1。
-
-注意,为了符合[语义化版本的说明](http://semver.org),增加 `minor` 版本时,`patch` 版本会被重设为 `0`,当增加 `major` 版本时,`minor` 和 `patch` 都将被重设为 `0`。
-
-```rust,editable
-use semver::{Version, SemVerError};
-
-fn main() -> Result<(), SemVerError> {
-    let mut parsed_version = Version::parse("0.2.6")?;
-
-    assert_eq!(
-        parsed_version,
-        Version {
-            major: 0,
-            minor: 2,
-            patch: 6,
-            pre: vec![],
-            build: vec![],
-        }
-    );
-
-    parsed_version.increment_patch();
-    assert_eq!(parsed_version.to_string(), "0.2.7");
-    println!("New patch release: v{}", parsed_version);
-
-    parsed_version.increment_minor();
-    assert_eq!(parsed_version.to_string(), "0.3.0");
-    println!("New minor release: v{}", parsed_version);
-
-    parsed_version.increment_major();
-    assert_eq!(parsed_version.to_string(), "1.0.0");
-    println!("New major release: v{}", parsed_version);
-
-    Ok(())
-}
-```
-
-### 解析一个复杂的版本号字符串
-这里的版本号字符串还将包含 `SemVer` 中定义的预发布和构建元信息。
-
-值得注意的是,为了符合 `SemVer` 的规则,构建元信息虽然会被解析,但是在做版本号比较时,该信息会被忽略。换而言之,即使两个版本号的构建字符串不同,它们的版本号依然可能相同。
-
-```rust,editable
-use semver::{Identifier, Version, SemVerError};
-
-fn main() -> Result<(), SemVerError> {
-    let version_str = "1.0.49-125+g72ee7853";
-    let parsed_version = Version::parse(version_str)?;
-
-    assert_eq!(
-        parsed_version,
-        Version {
-            major: 1,
-            minor: 0,
-            patch: 49,
-            pre: vec![Identifier::Numeric(125)],
-            build: vec![],
-        }
-    );
-    assert_eq!(
-        parsed_version.build,
-        vec![Identifier::AlphaNumeric(String::from("g72ee7853"))]
-    );
-
-    let serialized_version = parsed_version.to_string();
-    assert_eq!(&serialized_version, version_str);
-
-    Ok(())
-}
-```
-
-### 检查给定的版本号是否是预发布
-下面例子给出两个版本号,然后通过 [is_prerelease](https://docs.rs/semver/1.0.7/semver/struct.Version.html#method.is_prerelease) 判断哪个是预发布的版本号。
-
-```rust,editable
-use semver::{Version, SemVerError};
-
-fn main() -> Result<(), SemVerError> {
-    let version_1 = Version::parse("1.0.0-alpha")?;
-    let version_2 = Version::parse("1.0.0")?;
-
-    assert!(version_1.is_prerelease());
-    assert!(!version_2.is_prerelease());
-
-    Ok(())
-}
-```
-
-### 找出给定范围内的最新版本
-下面例子给出了一个版本号列表,我们需要找到其中最新的版本。
-
-```rust,editable
-#use error_chain::error_chain;
-
-use semver::{Version, VersionReq};
-
-#error_chain! {
-#    foreign_links {
-#        SemVer(semver::SemVerError);
-#        SemVerReq(semver::ReqParseError);
-#    }
-3}
-
-fn find_max_matching_version<'a, I>(version_req_str: &str, iterable: I) -> Result<Option<Version>>
-where
-    I: IntoIterator<Item = &'a str>,
-{
-    let vreq = VersionReq::parse(version_req_str)?;
-
-    Ok(
-        iterable
-            .into_iter()
-            .filter_map(|s| Version::parse(s).ok())
-            .filter(|s| vreq.matches(s))
-            .max(),
-    )
-}
-
-fn main() -> Result<()> {
-    assert_eq!(
-        find_max_matching_version("<= 1.0.0", vec!["0.9.0", "1.0.0", "1.0.1"])?,
-        Some(Version::parse("1.0.0")?)
-    );
-
-    assert_eq!(
-        find_max_matching_version(
-            ">1.2.3-alpha.3",
-            vec![
-                "1.2.3-alpha.3",
-                "1.2.3-alpha.4",
-                "1.2.3-alpha.10",
-                "1.2.3-beta.4",
-                "3.4.5-alpha.9",
-            ]
-        )?,
-        Some(Version::parse("1.2.3-beta.4")?)
-    );
-
-    Ok(())
-}
-```
-
-### 检查外部命令的版本号兼容性
-下面将通过 [Command](https://doc.rust-lang.org/std/process/struct.Command.html) 来执行系统命令 `git --version`,并对该系统命令返回的 `git` 版本号进行解析。
-
-```rust,editable
-#use error_chain::error_chain;
-
-use std::process::Command;
-use semver::{Version, VersionReq};
-
-#error_chain! {
-#    foreign_links {
-#        Io(std::io::Error);
-#        Utf8(std::string::FromUtf8Error);
-#        SemVer(semver::SemVerError);
-#        SemVerReq(semver::ReqParseError);
-#    }
-#}
-
-fn main() -> Result<()> {
-    let version_constraint = "> 1.12.0";
-    let version_test = VersionReq::parse(version_constraint)?;
-    let output = Command::new("git").arg("--version").output()?;
-
-    if !output.status.success() {
-        error_chain::bail!("Command executed with failing error code");
-    }
-
-    let stdout = String::from_utf8(output.stdout)?;
-    let version = stdout.split(" ").last().ok_or_else(|| {
-        "Invalid command output"
-    })?;
-    let parsed_version = Version::parse(version)?;
-
-    if !version_test.matches(&parsed_version) {
-        error_chain::bail!("Command version lower than minimum supported version (found {}, need {})",
-            parsed_version, version_constraint);
-    }
-
-    Ok(())
-}
-```
\ No newline at end of file
diff --git a/src/cookbook/encoding/csv.md b/src/cookbook/encoding/csv.md
deleted file mode 100644
index 19f2031e..00000000
--- a/src/cookbook/encoding/csv.md
+++ /dev/null
@@ -1 +0,0 @@
-# CSV
diff --git a/src/cookbook/encoding/intro.md b/src/cookbook/encoding/intro.md
deleted file mode 100644
index 0e61f3d0..00000000
--- a/src/cookbook/encoding/intro.md
+++ /dev/null
@@ -1 +0,0 @@
-# 编解码
diff --git a/src/cookbook/encoding/json.md b/src/cookbook/encoding/json.md
deleted file mode 100644
index 02ed5a24..00000000
--- a/src/cookbook/encoding/json.md
+++ /dev/null
@@ -1 +0,0 @@
-# JSON
diff --git a/src/cookbook/encoding/protobuf.md b/src/cookbook/encoding/protobuf.md
deleted file mode 100644
index 4fdb7ce0..00000000
--- a/src/cookbook/encoding/protobuf.md
+++ /dev/null
@@ -1 +0,0 @@
-# protobuf
diff --git a/src/cookbook/file/dir.md b/src/cookbook/file/dir.md
deleted file mode 100644
index c996c7a0..00000000
--- a/src/cookbook/file/dir.md
+++ /dev/null
@@ -1 +0,0 @@
-# 目录操作
diff --git a/src/cookbook/file/file.md b/src/cookbook/file/file.md
deleted file mode 100644
index 9286f56a..00000000
--- a/src/cookbook/file/file.md
+++ /dev/null
@@ -1 +0,0 @@
-# 文件读写
diff --git a/src/cookbook/file/intro.md b/src/cookbook/file/intro.md
deleted file mode 100644
index 0f8e6d56..00000000
--- a/src/cookbook/file/intro.md
+++ /dev/null
@@ -1 +0,0 @@
-# 文件系统 todo
diff --git a/src/cookbook/intro.md b/src/cookbook/intro.md
index 12f5f0e6..45419e9a 100644
--- a/src/cookbook/intro.md
+++ b/src/cookbook/intro.md
@@ -2,6 +2,12 @@
 
 对于开发者而言,CookBook 是非常实用的,几乎每一门编程语言都是如此。原因无他:聪明的开发者大部分时间不是在复制粘贴就是在复制粘贴的路上。而 CookBook 恰恰为各种实用场景提供了可供直接复制粘贴的代码,例如文件操作、随机数生成、命令行解析等等,
 
+由于这本书的章节非常多,为了不影响大家的整体阅读体验,请访问以下地址阅读。
+
+- 在线阅读: <a href="https://cookbook.rs" target="_blank">https://cookbook.rs</a>
+
+<br />
+
 > CookBook 的部分内容翻译自 [Rust CookBook](https://rust-lang-nursery.github.io/rust-cookbook/intro.html),但是内容并不相同,因为我们对部分内容进行了整合,最重要的是增加了大量实用库和代码片段
 
 
diff --git a/src/cookbook/protocol/grpc.md b/src/cookbook/protocol/grpc.md
deleted file mode 100644
index 37b724e9..00000000
--- a/src/cookbook/protocol/grpc.md
+++ /dev/null
@@ -1 +0,0 @@
-# gRPC
diff --git a/src/cookbook/protocol/http.md b/src/cookbook/protocol/http.md
deleted file mode 100644
index 03fe6a2d..00000000
--- a/src/cookbook/protocol/http.md
+++ /dev/null
@@ -1 +0,0 @@
-# HTTP
diff --git a/src/cookbook/protocol/intro.md b/src/cookbook/protocol/intro.md
deleted file mode 100644
index aaab51db..00000000
--- a/src/cookbook/protocol/intro.md
+++ /dev/null
@@ -1 +0,0 @@
-# 网络通信 todo
diff --git a/src/cookbook/protocol/tcp.md b/src/cookbook/protocol/tcp.md
deleted file mode 100644
index fd208cbf..00000000
--- a/src/cookbook/protocol/tcp.md
+++ /dev/null
@@ -1 +0,0 @@
-# TCP
diff --git a/src/cookbook/protocol/udp.md b/src/cookbook/protocol/udp.md
deleted file mode 100644
index 2827b96c..00000000
--- a/src/cookbook/protocol/udp.md
+++ /dev/null
@@ -1 +0,0 @@
-# UDP
diff --git a/src/cookbook/regexp.md b/src/cookbook/regexp.md
deleted file mode 100644
index b79ef0b1..00000000
--- a/src/cookbook/regexp.md
+++ /dev/null
@@ -1 +0,0 @@
-# 正则表达式 todo

From c302fe19362dce09f4c5db399dec1addc28a35fe Mon Sep 17 00:00:00 2001
From: sunface <cto@188.com>
Date: Tue, 5 Apr 2022 13:30:54 +0800
Subject: [PATCH 27/34] update readme.md

---
 README.md | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/README.md b/README.md
index 25b9919d..f9f4b0ac 100644
--- a/README.md
+++ b/README.md
@@ -37,12 +37,14 @@
 
 总之在写作过程中我们始终铭记初心:为中国用户打造一门**全面的、深入的、持续更新的** Rust 教程。 新手用来入门,老手用来提高,高手用来提升生产力。
 
-## 配套学习资源 
+## Rustt 翻译计划
+
+想要获取更多关于 Rust 的高质量技术文章、学习资料和新闻资讯嘛?请订阅 [Rustt 翻译计划](https://rustt.org)。
+
+## Rust语言周刊
+
+每周一发布,精选过去一周的技术文章、业界新闻、开源项目和 Rust 语言动态。与 Rustt 翻译计划不同,周刊不区分中英文,只是把最新产生的内容分门别类的呈现给广大读者。
 
-- [Rust语言实战](https://github.com/sunface/rust-by-practice),它是本书的配套练习册,提供了大量有挑战性的示例、练习和实践项目,帮助大家解决 Rust 语言从学习到实战的问题 — 毕竟这之间还隔着好几个 Go 语言的难度 :D
-- [Rust语言周刊](https://github.com/sunface/rust-weekly),每周一发布,精选过去一周的技术文章、业界新闻、开源项目和 Rust 语言动态
-- [Rust酷库推荐](https://github.com/sunface/fancy-rust),优秀项目很多,如何在茫茫码海中与它们相遇?相比 Awesome Rust,它能带给你全新的体验和选择
-- [Rustt 翻译组](https://rustt.org),目前最专业的 Rust 翻译组织,这里有 Rust 技术文章、学习教程和新闻资讯的高质量中文翻译
 
 ## 🏅 贡献者
 

From 47c37d1a85ae0e247cea6fe9dee093c5b86f5c11 Mon Sep 17 00:00:00 2001
From: sunface <cto@188.com>
Date: Tue, 5 Apr 2022 13:40:07 +0800
Subject: [PATCH 28/34] update toc

---
 book.toml                              |  2 +-
 src/SUMMARY.md                         |  5 +-
 src/{cookbook/intro.md => cookbook.md} |  0
 src/rust-weekly.md                     | 68 ++++++++++++++++++++++++++
 src/rustt.md                           | 13 +++++
 5 files changed, 85 insertions(+), 3 deletions(-)
 rename src/{cookbook/intro.md => cookbook.md} (100%)
 create mode 100644 src/rust-weekly.md
 create mode 100644 src/rustt.md

diff --git a/book.toml b/book.toml
index 22f16de7..0191baba 100644
--- a/book.toml
+++ b/book.toml
@@ -17,7 +17,7 @@ copy-js = true
 
 [output.html.fold]
 enable = true
-level = 2
+level = 1
 
 [rust]
 edition = "2021"   #在线运行用2021版本的
diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index 7358fe75..350f5961 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -164,8 +164,9 @@
 
 # 应用实战
 ---
-- [CookBook](cookbook/intro.md)
-
+- [Cookbook](cookbook.md)
+- [Rust 语言周刊](rust-weekly.md)
+- [Rustt 翻译计划](rustt.md)
 - [Rust 最佳实践](practice/intro.md)
   - [对抗编译检查](practice/fight-with-compiler/intro.md)
     - [生命周期](practice/fight-with-compiler/lifetime/intro.md)
diff --git a/src/cookbook/intro.md b/src/cookbook.md
similarity index 100%
rename from src/cookbook/intro.md
rename to src/cookbook.md
diff --git a/src/rust-weekly.md b/src/rust-weekly.md
new file mode 100644
index 00000000..6d617d66
--- /dev/null
+++ b/src/rust-weekly.md
@@ -0,0 +1,68 @@
+# Rust 语言周刊
+
+精选过去一周的文章、新闻、开源项目和 Rust 语言动态( 中文内容用 🇨🇳 进行标识 ),欢迎大家[订阅及查看往期回顾](https://github.com/studyrs/rust-weekly)。
+
+
+## 「Rust 语言周刊」 第 6 期 · 2022-04-02
+
+<img src="https://pica.zhimg.com/80/v2-23889bd3869ac6736256ac51ae4975d3_1440w.jpg">
+<h5 align="center">题图: Rust 嵌入式开发</h5>
+
+#### 精选文章
+
+1、 [Zh] [敢于要求更多 Rust 2024](https://github.com/studyrs/Rustt/blob/main/Articles/%5B2022-03-28%5D%20Rust%202024:敢于要求更多.md) - 翻译 [YuKun Liu](https://github.com/mrxiaozhuox)
+
+未来几年的 Rust 和社区应该怎么发展,可以简单总结为:敢于要求更多。
+
+2、[Zh] [Rust 嵌入式开发](https://github.com/studyrs/Rustt/blob/main/Articles/%5B2022-03-26%5D%20Rust%20嵌入式开发.md)  - 翻译 [Xiaobin.Liu](https://github.com/lxbwolf)
+
+本文展示了一些适用于嵌入式 Rust 的特性,总之, Rust 的高性能、高可靠和生产效率都非常适用于嵌入式系统。
+
+3、[dyn*: 尝试将 dyn 变成定长类型](https://smallcultfollowing.com/babysteps/blog/2022/03/29/dyn-can-we-make-dyn-sized/)
+
+三人行必能干翻诸葛亮,这不,作者和两个朋友在一次深入讨论后,突然诞生了这个奇妙的想法,最后还提交给了 Rust Team。作者还认为,一旦成功,那 `dyn Trait` 将更加好用、易用。
+
+4、[自修改代码](https://matklad.github.io/2022/03/26/self-modifying-code.html)
+
+对于 JIT 类似的动态机器码修改技术,大家应该都比较熟悉了,但是 Rust 中并没有。因此,作者想要通过一个简单的方法来替代宏去生成源代码。
+
+5、[异步解构器、异步泛型和完成式期约](https://sabrinajewson.org/blog/async-drop)
+
+本文的主要目标是为 Rust 设计一个系统以支持异步解构器( asynchronous destructors )。长文预警!
+
+6、[何时不应该使用 Rust](https://kerkour.com/why-not-rust)
+
+不出所料,文章内给出了快速原型设计的答案。短文预警!
+
+7、[Rust 交叉编译](https://kerkour.com/rust-cross-compilation)
+
+黑帽 Rust 作者又出手了,这次为我们带来关于交叉编译的优质内容。
+
+8、[小而美的 Rust Docker 镜像](https://azzamsa.com/n/rust-docker/)
+
+文章用 Rocket 框架写了一个 demo,然后将其打包成 Docker 镜像,最后的大小仅仅是 `8.38MB`,但... 算了,不剧透了,大家还是自己探索吧。
+
+9、[Book] [High Assurance Rust](https://highassurance.rs)
+
+由于我自己是开源书作者,因此对开源书有一种特别的偏爱。这本书主要关于如何开发高可靠、安全的软件服务,当然,书中还有一些计算机原理和架构设计的讲解。
+
+10、[Video] [Rust for Linux](https://www.youtube.com/watch?v=fVEeqo40IyQ)
+
+本视频将讲解目前 Linux 的 kernel 中,Rust 将扮演什么角色以及未来规划。
+
+#### 开源项目
+
+1、[生成你的 Github Profile](https://github.com/autarch/autarch)
+
+灵感来自于作者在简历中看到别人的炫酷 Github 个人首页展示,还写了[一篇文章](https://blog.urth.org/2022/03/28/yet-another-github-profile-generator/)。
+
+
+2、[fp-bindgen: 为全栈 WASM 插件生成相应的 binding](https://fiberplane.dev/blog/announcing-fp-bindgen/)
+
+全栈 WASM 插件是可以同时用在客户端和服务端的插件,而 `fp-bindgen` 让插件的创作变得更加简单,不仅如此,还提供了工具可以让它们在服务器上运行( hosting )。
+
+3、[BonsaiDB v0.4.0](https://bonsaidb.io/blog/bonsaidb-v0-4-0/)
+
+`BonsaiDB` 的目标是打造一个使用者友好的数据库,拥有大量常用的数据结构。但是之前的版本只支持异步 API,这个缺陷在新版本中得到了解决。
+
+
diff --git a/src/rustt.md b/src/rustt.md
new file mode 100644
index 00000000..a5df3788
--- /dev/null
+++ b/src/rustt.md
@@ -0,0 +1,13 @@
+# Rustt 翻译计划
+
+🥇Rustt 翻译计划,这里有国内最优质、最实时的 Rust 技术文章、学习资料和新闻资讯,欢迎大家[前往阅读和订阅](https://github.com/studyrs/Rustt)。
+
+## 最近优秀作品展
+
+| 中文名 |   翻译时间 | 作者 |
+| ------- | -------- | ----- |
+| [series][Rust 六边形架构](https://github.com/studyrs/Rustt/tree/main/Articles/%5B2022-04-03%5D%20Rust%20六边形架构) | 2022-04-04 | [trdthg](https://github.com/trdthg) |
+| [用 Rust 写 Devops 工具](https://github.com/studyrs/Rustt/blob/main/Articles/%5B2022-04-02%5D%20用%20Rust%20写%20DevOps%20工具.md) | 2022-04-03 | [Xiaobin.Liu](https://github.com/lxbwolf) |
+| [Rust 大佬给初学者的学习建议](https://github.com/studyrs/Rustt/blob/main/Articles/%5B2022-04-02%5D%20Rust%20大佬给初学者的学习建议.md) | 2022-04-02 | [Asura](https://github.com/asur4s) |
+| [Rust 背后并不是公司](https://github.com/studyrs/Rustt/blob/main/Articles/%5B2022-04-01%5D%20Rust%20背后并不是公司.md) | 2022-04-01 | [子殊](https://github.com/allenli178) |
+| [在 Rust 中使用 epoll 实现非阻塞 IO](https://github.com/studyrs/Rustt/blob/main/Articles/%5B2022-03-29%5D%20在%20Rust%20中使用%20epoll%20实现基本的非阻塞%20IO.md) | 2022-03-29 | [BK0717](https://github.com/hyuuko) | 

From 5fcde5402084eed9ed8a5c387b1ba84199ae924f Mon Sep 17 00:00:00 2001
From: sunface <cto@188.com>
Date: Tue, 5 Apr 2022 13:56:55 +0800
Subject: [PATCH 29/34] update toc

---
 src/SUMMARY.md   | 11 +++++++----
 theme/style2.css |  8 ++++++++
 2 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index 350f5961..2cf408a1 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -2,9 +2,12 @@
 
 [Rust语言圣经](about-book.md)
 [进入 Rust 编程世界](into-rust.md)
-[AWS 为何这么喜欢 Rust?](usecases/aws-rust.md)
 [快速查询入口](index-list.md)
 
+---
+[Rust Cookbook](cookbook.md)
+[Rust 语言周刊](rust-weekly.md)
+[Rustt 翻译计划](rustt.md)
 
 # 快速开始
 ---
@@ -164,9 +167,9 @@
 
 # 应用实战
 ---
-- [Cookbook](cookbook.md)
-- [Rust 语言周刊](rust-weekly.md)
-- [Rustt 翻译计划](rustt.md)
+- [企业落地实践](usecases/intro.md)
+  - [AWS 为何这么喜欢 Rust?](usecases/aws-rust.md)
+
 - [Rust 最佳实践](practice/intro.md)
   - [对抗编译检查](practice/fight-with-compiler/intro.md)
     - [生命周期](practice/fight-with-compiler/lifetime/intro.md)
diff --git a/theme/style2.css b/theme/style2.css
index e4d5a8cd..eacea892 100644
--- a/theme/style2.css
+++ b/theme/style2.css
@@ -97,4 +97,12 @@ table {
 /* Fix on mobile device */
 code {
     word-break: break-word;
+}
+
+/* 修改书侧边目录的区域分隔行样式 */
+
+.chapter .spacer {
+    background-color: #99CCFF;
+    height: 2px;
+    margin-top: 8px;
 }
\ No newline at end of file

From b51f43c3845091a9a3000afba0b15065720c0fbc Mon Sep 17 00:00:00 2001
From: sunface <cto@188.com>
Date: Tue, 5 Apr 2022 13:57:15 +0800
Subject: [PATCH 30/34] update toc

---
 book.toml                        | 2 +-
 theme/{style2.css => style3.css} | 0
 2 files changed, 1 insertion(+), 1 deletion(-)
 rename theme/{style2.css => style3.css} (100%)

diff --git a/book.toml b/book.toml
index 0191baba..00c73b29 100644
--- a/book.toml
+++ b/book.toml
@@ -5,7 +5,7 @@ title = "Rust语言圣经(Rust Course)"
 src = "src"
 
 [output.html]
-additional-css = ["theme/style2.css"]
+additional-css = ["theme/style3.css"]
 additional-js = ["assets/custom.js", "assets/bigPicture.js"]
 git-repository-url = "https://github.com/sunface/rust-course"
 edit-url-template = "https://github.com/sunface/rust-course/edit/main/{path}"
diff --git a/theme/style2.css b/theme/style3.css
similarity index 100%
rename from theme/style2.css
rename to theme/style3.css

From 7f131ae84e2d01e1800d9c83be18a14af31df504 Mon Sep 17 00:00:00 2001
From: sunface <cto@188.com>
Date: Tue, 5 Apr 2022 14:32:43 +0800
Subject: [PATCH 31/34] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AB=A0=E8=8A=82=20[c?=
 =?UTF-8?q?ookbook=20-=20=E5=AD=97=E7=AC=A6=E9=9B=86]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 theme/style3.css      | 2 +-
 内容变更记录.md | 4 ++++
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/theme/style3.css b/theme/style3.css
index eacea892..acf81bc9 100644
--- a/theme/style3.css
+++ b/theme/style3.css
@@ -105,4 +105,4 @@ code {
     background-color: #99CCFF;
     height: 2px;
     margin-top: 8px;
-}
\ No newline at end of file
+}
diff --git a/内容变更记录.md b/内容变更记录.md
index 1cd8c8f0..babd272c 100644
--- a/内容变更记录.md
+++ b/内容变更记录.md
@@ -1,6 +1,10 @@
 # ChangeLog
 记录一些值得注意的变更。
 
+## 2022-04-05
+
+- 新增章节:[Cookbook - 字符编码](https://cookbook.rs/encoding/strings.html)
+
 ## 2022-04-04 
 
 - 新增章节: [Cookbook - 使用 rayon 并行处理数据](https://course.rs/cookbook/cocurrency/parallel.html)

From 5afd0d19edcd2bef43c8082d48f44a4750833e3c Mon Sep 17 00:00:00 2001
From: sunface <cto@188.com>
Date: Tue, 5 Apr 2022 14:50:05 +0800
Subject: [PATCH 32/34] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AB=A0=E8=8A=82=20[c?=
 =?UTF-8?q?ookbook=20-=20CSV=E5=A4=84=E7=90=86]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 内容变更记录.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/内容变更记录.md b/内容变更记录.md
index babd272c..bbca8238 100644
--- a/内容变更记录.md
+++ b/内容变更记录.md
@@ -4,6 +4,7 @@
 ## 2022-04-05
 
 - 新增章节:[Cookbook - 字符编码](https://cookbook.rs/encoding/strings.html)
+- 新增章节:[Cookbook - CSV处理](https://cookbook.rs/encoding/csv.html)
 
 ## 2022-04-04 
 

From b931b8bff01be9eff2d28ea9f10423d73840f8b3 Mon Sep 17 00:00:00 2001
From: sunface <cto@188.com>
Date: Tue, 5 Apr 2022 19:49:03 +0800
Subject: [PATCH 33/34] rename cookbook to Cook Rust

---
 src/SUMMARY.md                   |  2 +-
 src/cookbook.md                  | 30 +++++++++++++++++++++++-------
 src/practice/third-party-libs.md |  2 +-
 3 files changed, 25 insertions(+), 9 deletions(-)

diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index 2cf408a1..5cc4abae 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -5,7 +5,7 @@
 [快速查询入口](index-list.md)
 
 ---
-[Rust Cookbook](cookbook.md)
+[Cook Rust: Awesome + Cookbook](cookbook.md)
 [Rust 语言周刊](rust-weekly.md)
 [Rustt 翻译计划](rustt.md)
 
diff --git a/src/cookbook.md b/src/cookbook.md
index 45419e9a..7fc87b10 100644
--- a/src/cookbook.md
+++ b/src/cookbook.md
@@ -1,25 +1,41 @@
-# 场景化用例
+<h1 align="center">Cook Rust</h1>
 
-对于开发者而言,CookBook 是非常实用的,几乎每一门编程语言都是如此。原因无他:聪明的开发者大部分时间不是在复制粘贴就是在复制粘贴的路上。而 CookBook 恰恰为各种实用场景提供了可供直接复制粘贴的代码,例如文件操作、随机数生成、命令行解析等等,
+<div align="center">
+    <img height="200px" src="https://github.com/sunface/rust-cookbook/blob/main/assets/banner1.png?raw=true">
+</div>
+   
+<div align="center">
 
-由于这本书的章节非常多,为了不影响大家的整体阅读体验,请访问以下地址阅读。
+    
+在线阅读: [https://cook.rs](https://cook.rs)
+</div>
 
-- 在线阅读: <a href="https://cookbook.rs" target="_blank">https://cookbook.rs</a>
+学习一门语言、做一个项目就像烹饪一顿美食一样,你需要往项目中添加许多调味料,而 Cook Rust 就是教大家如何烹饪一个优秀的 Rust 项目。
 
-<br />
+| 烹饪美食 | Cook Rust |
+| --- | --- |
+| 找到合适的厨具、调料、食材 | 为项目挑选 Awesome 依赖库 |
+| 按照食谱做好一道道菜,最终呈现一桌大餐 | 在 Cookbook 中查询实用的代码片段,直接复制到项目中,最终快速搭建好一个项目 | 
 
-> CookBook 的部分内容翻译自 [Rust CookBook](https://rust-lang-nursery.github.io/rust-cookbook/intro.html),但是内容并不相同,因为我们对部分内容进行了整合,最重要的是增加了大量实用库和代码片段
+可以看出 `Cook Rust` = `Awesome Rust` + `Rust Cookbook`,**在这里你可以找到各种优秀的依赖库和代码片段**,无论是学习还是快速搭建项目,本书都可以助你一臂之力!
+
+关于 Awesome,相信大家已经非常熟悉。但目前最火的 awesome-rust 项目有一个非常大的问题:里面的项目鱼龙混杂,因为它的目的是列出所有项目,但对于用户而言,更想看到的是可以在生产中使用的、稳定更新的优秀项目。
+
+对于开发者而言,Cookbook 非常实用的,几乎每一门编程语言都是如此。原因无他:聪明的开发者大部分时间不是在复制粘贴就是在复制粘贴的路上。而 CookBook 恰恰为各种实用场景提供了可供直接复制粘贴的代码,例如网络协议、数据库和文件操作、随机数生成、命令行解析等。既可以用于学习 Rust ,还能大幅提升你的编码效率。
+
+> Cookbook 的部分内容翻译自 [Rust CookBook](https://rust-lang-nursery.github.io/rust-cookbook/intro.html),但由于这本英文书更新不太活跃,导致了内容存在较多的遗漏或过期,因此我们并没有完全照搬翻译这本书的内容,而是在此基础上增加了大量新的实用库和代码片段,希望大家喜欢
 
 
 ## 这本书的读者
 本书适合所有程度的 Rust 开发者使用:
 
-- 新手用来熟悉生态和常用库
+- 新手用来了解 Rust 的常用库和常用代码片段
 - 老手在写代码时,可以直接用来复制粘贴,大幅提升工作效率
 
 毕竟咱不是在面试造飞机,谁脑袋中能记住文件操作的各种细节,对不?
 
 ## 怎么使用
+
 Cookbook 中的代码都是完整的,换而言之,这些代码片段包含了 `fn main` 函数,可以直接运行,如果你是拷贝到自己的代码中,请注意拷贝相应的代码部分,而不是全盘复制。
 
 同时,这些代码( 大部分 )支持在线编辑和运行,大家无需复制到 IDE 中即可进行把玩研究。
diff --git a/src/practice/third-party-libs.md b/src/practice/third-party-libs.md
index 9b187fae..c137fe6b 100644
--- a/src/practice/third-party-libs.md
+++ b/src/practice/third-party-libs.md
@@ -4,7 +4,7 @@
 
 本文就分门别类的精心挑选了一些非常适合日常开发使用的三方库,同时针对优缺点、社区活跃等进行了评价,同一个类别的库,按照**推荐度优先级降序排列**,希望大家能喜欢。
 
-> 本文节选自[Fancy Rust](https://fancy.rs), 一个Rust酷库推荐项目, 里面精选了各个领域的好项目,无论是学习还是工作使用,都能助你一臂之力。
+> 本文节选自[Cook Rust](https://cook.rs)
 
 ## 目录
 - 日常开发常用的Rust库: 

From a63701e542a73f074a531d0f5353bd8d670257ff Mon Sep 17 00:00:00 2001
From: sunface <cto@188.com>
Date: Tue, 5 Apr 2022 20:02:07 +0800
Subject: [PATCH 34/34] rename cookbook to Cook Rust

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index f9f4b0ac..7e8ad63a 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@
 
 - **规避陷阱和对抗编译器**,只有真的上手写过一长段时间 Rust 项目,才知道该如何规避常见的陷阱以及解决一些难搞的编译器错误,而本书将帮助你大大缩短这个过程,提前规避这些问题
 
-- **[Cookbook](https://cookbook.rs)**,涵盖多个应用场景的实战代码片段,程序员上网查询文件操作、正则解析、数据库操作是常事,没有人能记住所有代码,而 Cook Book 可解君忧,Ctrl + C/V 走天下
+- **[Cookbook](https://cook.rs)**,涵盖多个应用场景的实战代码片段,程序员上网查询文件操作、正则解析、数据库操作是常事,没有人能记住所有代码,而 Cook Book 可解君忧,Ctrl + C/V 走天下
 
 - **[配套练习题](https://github.com/sunface/rust-by-practice)**,像学习一门大学课程一样学习 Rust 是一种什么感觉?*Rust语言圣经 + Rust语言实战* 双剑合璧,给你最极致的学习体验