lyq.me@qq.com 8 months ago
parent
commit
c2978fac59
52 changed files with 4957 additions and 5 deletions
  1. 2 2
      docs/book.toml
  2. 37 0
      docs/src/algos/awesome.md
  3. 77 0
      docs/src/algos/compression/tar.md
  4. 57 0
      docs/src/algos/cryptography/encryption.md
  5. 71 0
      docs/src/algos/cryptography/hashing.md
  6. 2 0
      docs/src/algos/datastructures/awesome.md
  7. 47 0
      docs/src/algos/datastructures/bitfield.md
  8. 43 0
      docs/src/algos/math/complex.md
  9. 176 0
      docs/src/algos/math/linear-algebra.md
  10. 24 0
      docs/src/algos/math/misc.md
  11. 176 0
      docs/src/algos/math/statistics.md
  12. 59 0
      docs/src/algos/math/trigonometry.md
  13. 155 0
      docs/src/algos/randomness.md
  14. 84 0
      docs/src/algos/sorting.md
  15. 1 0
      docs/src/awesome-empowering-js.md
  16. 1 0
      docs/src/awesome-superstar.md
  17. 1 0
      docs/src/base/dev-env.md
  18. 50 0
      docs/src/cmd/ansi.md
  19. 222 0
      docs/src/cmd/awesome.md
  20. 70 0
      docs/src/cmd/parsing.md
  21. 201 0
      docs/src/cocurrency/parallel.md
  22. 336 0
      docs/src/cocurrency/threads.md
  23. 123 0
      docs/src/database/postgres.md
  24. 136 0
      docs/src/database/sqlite.md
  25. 69 0
      docs/src/datetime/duration.md
  26. 114 0
      docs/src/datetime/parsing.md
  27. 1 0
      docs/src/dev/intro.md
  28. 1 0
      docs/src/dev/logs.md
  29. 1 0
      docs/src/dev/profile.md
  30. 192 0
      docs/src/devtools/build-tools.md
  31. 140 0
      docs/src/devtools/config-log.md
  32. 127 0
      docs/src/devtools/log.md
  33. 186 0
      docs/src/devtools/version.md
  34. 277 0
      docs/src/encoding/csv.md
  35. 1 0
      docs/src/encoding/intro.md
  36. 1 0
      docs/src/encoding/json.md
  37. 1 0
      docs/src/encoding/protobuf.md
  38. 106 0
      docs/src/encoding/strings.md
  39. 142 0
      docs/src/encoding/structured.md
  40. 242 0
      docs/src/files/dir.md
  41. 94 0
      docs/src/files/read-write.md
  42. 31 0
      docs/src/memory/global-vars.md
  43. 71 0
      docs/src/os/awesome.md
  44. 228 0
      docs/src/os/command.md
  45. 11 0
      docs/src/os/processor.md
  46. 28 0
      docs/src/protocols/tcpip.md
  47. 202 0
      docs/src/text/regex.md
  48. 75 0
      docs/src/text/string.md
  49. 34 0
      docs/src/web/scraping.md
  50. 315 0
      docs/theme/index.hbs
  51. 113 0
      docs/theme/style1.css
  52. 3 3
      hello_world/src/main.rs

+ 2 - 2
docs/book.toml

@@ -7,8 +7,8 @@ title = "rust-note"
 
 [output.html]
 no-section-label=true
-additional-css=["./css/custom.css"]
-additional-js=["./js/custom.js"]
+#additional-css=["./css/custom.css"]
+#additional-js=["./js/custom.js"]
 git-repository-url="https://github.com/yoqi/rust-note"
 edit-url-template="https://github.com/yoqi/rust-note/edit/main/{path}"
 

+ 37 - 0
docs/src/algos/awesome.md

@@ -0,0 +1,37 @@
+# Awesome 算法
+
+## 通用算法
+---
+### rust-algorithms
+
+[rust-algorithms](https://github.com/EbTech/rust-algorithms)收集了一些经典的算法和数据结构,更强调算法实现的美观性,因此该库更适用于教学目的,请不要把它当成一个实用算法库在生产环境使用。
+
+### TheAlgorithms/Rust
+[TheAlgorithms/Rust](https://github.com/TheAlgorithms/Rust)项目所属的[组织](https://github.com/TheAlgorithms)使用各种语言实现了多种算法,但是仅适用于演示的目的。
+
+## Leetcode
+---
+
+### rustgym
+[rustgym](https://github.com/warycat/rustgym) 实现了相当多的 leetcode 和 Avent of Code 题解。
+
+## 分布式算法
+---
+
+### raft-rs
+
+[raft-rs](https://github.com/tikv/raft-rs) 是由 Tikv 提供的 Raft 分布式算法实现。[Raft](https://raft.github.io)是一个强一致性的分布式算法,比 Paxos 协议更简单、更好理解
+
+## 密码学
+---
+
+### Rust Crypto
+
+[Rust Crypto](https://github.com/RustCrypto)提供了一些常用的密码学算法实现,更新较为活跃。
+
+## 专用算法
+---
+
+### rust-bio
+
+[rust-bio](https://github.com/rust-bio/rust-bio) 有常用的生物信息学所需的算法和数据结构。

+ 77 - 0
docs/src/algos/compression/tar.md

@@ -0,0 +1,77 @@
+# 使用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(())
+}
+```

+ 57 - 0
docs/src/algos/cryptography/encryption.md

@@ -0,0 +1,57 @@
+# 加密
+
+### 使用  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(())
+}
+```

+ 71 - 0
docs/src/algos/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(())
+}
+```

+ 2 - 0
docs/src/algos/datastructures/awesome.md

@@ -0,0 +1,2 @@
+# 数据结构
+

+ 47 - 0
docs/src/algos/datastructures/bitfield.md

@@ -0,0 +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");
+}
+```

+ 43 - 0
docs/src/algos/math/complex.md

@@ -0,0 +1,43 @@
+# 复数
+
+### 创建复数
+[num::complex::Complex](https://autumnai.github.io/cuticula/num/complex/struct.Complex.html) 可以帮助我们创建复数,其中实部和虚部必须是一样的类型。
+
+```rust,editable
+fn main() {
+    let complex_integer = num::complex::Complex::new(10, 20);
+    let complex_float = num::complex::Complex::new(10.1, 20.1);
+
+    println!("Complex integer: {}", complex_integer);
+    println!("Complex float: {}", complex_float);
+}
+```
+
+### 复数相加
+复数计算和 Rust 基本类型的计算并无区别。
+
+```rust,editable
+fn main() {
+    let complex_num1 = num::complex::Complex::new(10.0, 20.0); // Must use floats
+    let complex_num2 = num::complex::Complex::new(3.1, -4.2);
+
+    let sum = complex_num1 + complex_num2;
+
+    println!("Sum: {}", sum);
+}
+```
+
+### 数学函数
+在 [num::complex::Complex](https://autumnai.github.io/cuticula/num/complex/struct.Complex.html) 中定义了一些内置的数学函数,可用于对复数进行数学运算。
+
+```rust,editable
+use std::f64::consts::PI;
+use num::complex::Complex;
+
+fn main() {
+    let x = Complex::new(0.0, 2.0*PI);
+
+    println!("e^(2i * pi) = {}", x.exp()); // =~1
+}
+```
+

+ 176 - 0
docs/src/algos/math/linear-algebra.md

@@ -0,0 +1,176 @@
+# 线性代数
+
+### 矩阵相加
+
+使用 [ndarray::arr2](https://docs.rs/ndarray/*/ndarray/fn.arr2.html) 可以创建二阶矩阵,并计算它们的和。
+
+```rust,editable
+use ndarray::arr2;
+
+fn main() {
+    let a = arr2(&[[1, 2, 3],
+                   [4, 5, 6]]);
+
+    let b = arr2(&[[6, 5, 4],
+                   [3, 2, 1]]);
+
+    // 借用 a 和 b,求和后生成新的矩阵 sum
+    let sum = &a + &b;
+
+    println!("{}", a);
+    println!("+");
+    println!("{}", b);
+    println!("=");
+    println!("{}", sum);
+}
+```
+
+### 矩阵相乘
+
+[ndarray::ArrayBase::dot](https://docs.rs/ndarray/0.15.4/ndarray/struct.ArrayBase.html#method.dot-1) 可以用于计算矩阵乘法。
+
+```rust,editable
+use ndarray::arr2;
+
+fn main() {
+    let a = arr2(&[[1, 2, 3],
+                   [4, 5, 6]]);
+
+    let b = arr2(&[[6, 3],
+                   [5, 2],
+                   [4, 1]]);
+
+    println!("{}", a.dot(&b));
+}
+```
+
+### 标量、向量、矩阵相乘
+
+在 `ndarry`中,1 阶数组根据上下文既可以作为行向量也可以作为列向量。如果对你来说,这个行或列的方向很重要,可以考虑使用一行或一列的 2 阶数组来表示。
+
+在下面例子中,由于 1 阶数组处于乘号的右边位置,因此 `dot` 会把它当成列向量来处理。
+
+```rust,editable
+use ndarray::{arr1, arr2, Array1};
+
+fn main() {
+    let scalar = 4;
+
+    let vector = arr1(&[1, 2, 3]);
+
+    let matrix = arr2(&[[4, 5, 6],
+                        [7, 8, 9]]);
+
+    let new_vector: Array1<_> = scalar * vector;
+    println!("{}", new_vector);
+
+    let new_matrix = matrix.dot(&new_vector);
+    println!("{}", new_matrix);
+}
+```
+
+### 向量比较
+
+浮点数通常是不精确的,因此比较浮点数不是一件简单的事。[approx](https://docs.rs/approx/*/approx/index.html) 提供的 [assert_abs_diff_eq!](https://docs.rs/approx/0.5.1/approx/macro.assert_abs_diff_eq.html) 宏提供了方便的按元素比较的方式。为了使用 `approx` ,你需要在 `ndarray` 的依赖中开启相应的 feature:例如,在 `Cargo.toml` 中修改 `ndarray` 的依赖引入为 `ndarray = { version = "0.13", features = ["approx"] }`。
+
+```rust,editable
+use approx::assert_abs_diff_eq;
+use ndarray::Array;
+
+fn main() {
+  let a = Array::from(vec![1., 2., 3., 4., 5.]);
+  let b = Array::from(vec![5., 4., 3., 2., 1.]);
+  let mut c = Array::from(vec![1., 2., 3., 4., 5.]);
+  let mut d = Array::from(vec![5., 4., 3., 2., 1.]);
+  
+  // 消耗 a 和 b 的所有权
+  let z = a + b;
+  // 借用 c 和 d
+  let w =  &c + &d;
+
+  assert_abs_diff_eq!(z, Array::from(vec![6., 6., 6., 6., 6.]));
+
+  println!("c = {}", c);
+  c[0] = 10.;
+  d[1] = 10.;
+
+  assert_abs_diff_eq!(w, Array::from(vec![6., 6., 6., 6., 6.]));
+
+}
+```
+
+### 向量范数( norm )
+
+需要注意的是 `Array` 和 `ArrayView` 都是 `ArrayBase` 的别名。因此一个更通用的参数应该是 `&ArrayBase<S, Ix1> where S: Data`,特别是在你提供一个公共 API 给其它用户时,但由于咱们是内部使用,因此更精准的 `ArrayView1<f64>` 会更适合。
+
+```rust,editable
+use ndarray::{array, Array1, ArrayView1};
+
+fn l1_norm(x: ArrayView1<f64>) -> f64 {
+    x.fold(0., |acc, elem| acc + elem.abs())
+}
+
+fn l2_norm(x: ArrayView1<f64>) -> f64 {
+    x.dot(&x).sqrt()
+}
+
+fn normalize(mut x: Array1<f64>) -> Array1<f64> {
+    let norm = l2_norm(x.view());
+    x.mapv_inplace(|e| e/norm);
+    x
+}
+
+fn main() {
+    let x = array![1., 2., 3., 4., 5.];
+    println!("||x||_2 = {}", l2_norm(x.view()));
+    println!("||x||_1 = {}", l1_norm(x.view()));
+    println!("Normalizing x yields {:?}", normalize(x));
+}
+```
+
+### 矩阵的逆变换
+例子中使用 [nalgebra::Matrix3](https://docs.rs/nalgebra/*/nalgebra/base/type.Matrix3.html) 创建一个 3x3 的矩阵,然后尝试对其进行逆变换,获取一个逆矩阵。
+
+```rust,editable
+use nalgebra::Matrix3;
+
+fn main() {
+    let m1 = Matrix3::new(2.0, 1.0, 1.0, 3.0, 2.0, 1.0, 2.0, 1.0, 2.0);
+    println!("m1 = {}", m1);
+    match m1.try_inverse() {
+        Some(inv) => {
+            println!("The inverse of m1 is: {}", inv);
+        }
+        None => {
+            println!("m1 is not invertible!");
+        }
+    }
+}
+```
+
+### 序列/反序列化一个矩阵
+
+下面将展示如何将矩阵序列化为 JSON ,然后再反序列化为原矩阵。
+
+```rust,editable
+extern crate nalgebra;
+extern crate serde_json;
+
+use nalgebra::DMatrix;
+
+fn main() -> Result<(), std::io::Error> {
+    let row_slice: Vec<i32> = (1..5001).collect();
+    let matrix = DMatrix::from_row_slice(50, 100, &row_slice);
+
+    // 序列化矩阵
+    let serialized_matrix = serde_json::to_string(&matrix)?;
+
+    // 反序列化
+    let deserialized_matrix: DMatrix<i32> = serde_json::from_str(&serialized_matrix)?;
+
+    // 验证反序列化后的矩阵跟原始矩阵相等
+    assert!(deserialized_matrix == matrix);
+
+    Ok(())
+}
+```

+ 24 - 0
docs/src/algos/math/misc.md

@@ -0,0 +1,24 @@
+# 杂项
+
+### 大整数 Big int
+使用 [BitInt](https://docs.rs/num/0.2.0/num/struct.BigInt.html) 可以对超过 128bit  的整数进行计算。
+
+```rust,editable
+use num::bigint::{BigInt, ToBigInt};
+
+fn factorial(x: i32) -> BigInt {
+    if let Some(mut factorial) = 1.to_bigint() {
+        for i in 1..=x {
+            factorial = factorial * i;
+        }
+        factorial
+    }
+    else {
+        panic!("Failed to calculate factorial!");
+    }
+}
+
+fn main() {
+    println!("{}! equals {}", 100, factorial(100));
+}
+```

+ 176 - 0
docs/src/algos/math/statistics.md

@@ -0,0 +1,176 @@
+# 统计
+
+### 测量中心趋势
+
+下面的一些例子为 Rust 数组中的数据计算它们的中心趋势。
+
+#### 平均值
+首先计算的是平均值。
+
+```rust,editable
+fn main() {
+    let data = [3, 1, 6, 1, 5, 8, 1, 8, 10, 11];
+
+    let sum = data.iter().sum::<i32>() as f32;
+    let count = data.len();
+
+    let mean = match count {
+       positive if positive > 0 => Some(sum  / count as f32),
+       _ => None
+    };
+
+    println!("Mean of the data is {:?}", mean);
+}
+```
+
+#### 中位数
+下面使用快速选择算法来计算中位数。该算法只会对可能包含中位数的数据分区进行排序,从而避免了对所有数据进行全排序。
+
+```rust,editable
+use std::cmp::Ordering;
+
+fn partition(data: &[i32]) -> Option<(Vec<i32>, i32, Vec<i32>)> {
+    match data.len() {
+        0 => None,
+        _ => {
+            let (pivot_slice, tail) = data.split_at(1);
+            let pivot = pivot_slice[0];
+            let (left, right) = tail.iter()
+                .fold((vec![], vec![]), |mut splits, next| {
+                    {
+                        let (ref mut left, ref mut right) = &mut splits;
+                        if next < &pivot {
+                            left.push(*next);
+                        } else {
+                            right.push(*next);
+                        }
+                    }
+                    splits
+                });
+
+            Some((left, pivot, right))
+        }
+    }
+}
+
+fn select(data: &[i32], k: usize) -> Option<i32> {
+    let part = partition(data);
+
+    match part {
+        None => None,
+        Some((left, pivot, right)) => {
+            let pivot_idx = left.len();
+
+            match pivot_idx.cmp(&k) {
+                Ordering::Equal => Some(pivot),
+                Ordering::Greater => select(&left, k),
+                Ordering::Less => select(&right, k - (pivot_idx + 1)),
+            }
+        },
+    }
+}
+
+fn median(data: &[i32]) -> Option<f32> {
+    let size = data.len();
+
+    match size {
+        even if even % 2 == 0 => {
+            let fst_med = select(data, (even / 2) - 1);
+            let snd_med = select(data, even / 2);
+
+            match (fst_med, snd_med) {
+                (Some(fst), Some(snd)) => Some((fst + snd) as f32 / 2.0),
+                _ => None
+            }
+        },
+        odd => select(data, odd / 2).map(|x| x as f32)
+    }
+}
+
+fn main() {
+    let data = [3, 1, 6, 1, 5, 8, 1, 8, 10, 11];
+
+    let part = partition(&data);
+    println!("Partition is {:?}", part);
+
+    let sel = select(&data, 5);
+    println!("Selection at ordered index {} is {:?}", 5, sel);
+
+    let med = median(&data);
+    println!("Median is {:?}", med);
+}
+```
+
+#### 众数( mode )
+下面使用了 `HashMap` 对不同数字出现的次数进行了分别统计。
+
+```rust,editable
+use std::collections::HashMap;
+
+fn main() {
+    let data = [3, 1, 6, 1, 5, 8, 1, 8, 10, 11];
+
+    let frequencies = data.iter().fold(HashMap::new(), |mut freqs, value| {
+        *freqs.entry(value).or_insert(0) += 1;
+        freqs
+    });
+
+    let mode = frequencies
+        .into_iter()
+        .max_by_key(|&(_, count)| count)
+        .map(|(value, _)| *value);
+
+    println!("Mode of the data is {:?}", mode);
+}
+```
+
+### 标准偏差
+
+下面一起来看看该如何计算一组测量值的标准偏差和 z-score。
+
+```rust,editable
+fn mean(data: &[i32]) -> Option<f32> {
+    let sum = data.iter().sum::<i32>() as f32;
+    let count = data.len();
+
+    match count {
+        positive if positive > 0 => Some(sum / count as f32),
+        _ => None,
+    }
+}
+
+fn std_deviation(data: &[i32]) -> Option<f32> {
+    match (mean(data), data.len()) {
+        (Some(data_mean), count) if count > 0 => {
+            let variance = data.iter().map(|value| {
+                let diff = data_mean - (*value as f32);
+
+                diff * diff
+            }).sum::<f32>() / count as f32;
+
+            Some(variance.sqrt())
+        },
+        _ => None
+    }
+}
+
+fn main() {
+    let data = [3, 1, 6, 1, 5, 8, 1, 8, 10, 11];
+
+    let data_mean = mean(&data);
+    println!("Mean is {:?}", data_mean);
+
+    let data_std_deviation = std_deviation(&data);
+    println!("Standard deviation is {:?}", data_std_deviation);
+
+    let zscore = match (data_mean, data_std_deviation) {
+        (Some(mean), Some(std_deviation)) => {
+            let diff = data[4] as f32 - mean;
+
+            Some(diff / std_deviation)
+        },
+        _ => None
+    };
+    println!("Z-score of data at index 4 (with value {}) is {:?}", data[4], zscore);
+}
+```

+ 59 - 0
docs/src/algos/math/trigonometry.md

@@ -0,0 +1,59 @@
+# 三角函数
+
+### 三角形边长计算
+计算角为 2 弧度、对边长度为 80 的直角三角形的斜边长度。
+
+```rust,editable
+fn main() {
+    let angle: f64 = 2.0;
+    let side_length = 80.0;
+
+    let hypotenuse = side_length / angle.sin();
+
+    println!("Hypotenuse: {}", hypotenuse);
+}
+```
+
+### 验证 tan = sin / cos
+
+```rust,editable
+fn main() {
+    let x: f64 = 6.0;
+
+    let a = x.tan();
+    let b = x.sin() / x.cos();
+
+    assert_eq!(a, b);
+}
+```
+
+### 地球上两点间的距离
+下面的代码使用 [Haversine 公式](https://blog.csdn.net/Hardict/article/details/105267473) 计算地球上两点之间的公里数。
+
+```rust,editable
+fn main() {
+    let earth_radius_kilometer = 6371.0_f64;
+    let (paris_latitude_degrees, paris_longitude_degrees) = (48.85341_f64, -2.34880_f64);
+    let (london_latitude_degrees, london_longitude_degrees) = (51.50853_f64, -0.12574_f64);
+
+    let paris_latitude = paris_latitude_degrees.to_radians();
+    let london_latitude = london_latitude_degrees.to_radians();
+
+    let delta_latitude = (paris_latitude_degrees - london_latitude_degrees).to_radians();
+    let delta_longitude = (paris_longitude_degrees - london_longitude_degrees).to_radians();
+
+    let central_angle_inner = (delta_latitude / 2.0).sin().powi(2)
+        + paris_latitude.cos() * london_latitude.cos() * (delta_longitude / 2.0).sin().powi(2);
+    let central_angle = 2.0 * central_angle_inner.sqrt().asin();
+
+    let distance = earth_radius_kilometer * central_angle;
+
+    println!(
+        "Distance between Paris and London on the surface of Earth is {:.1} kilometers",
+        distance
+    );
+}
+```
+
+
+

+ 155 - 0
docs/src/algos/randomness.md

@@ -0,0 +1,155 @@
+# 生成随机值
+
+### 生成随机数
+
+使用 [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);
+}
+```

+ 84 - 0
docs/src/algos/sorting.md

@@ -0,0 +1,84 @@
+## 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),
+        ]);
+
+}
+```

+ 1 - 0
docs/src/awesome-empowering-js.md

@@ -0,0 +1 @@
+# 函数,结构体(类)

+ 1 - 0
docs/src/awesome-superstar.md

@@ -0,0 +1 @@
+# Rust 数据结构

+ 1 - 0
docs/src/base/dev-env.md

@@ -0,0 +1 @@
+# 开发环境搭建

+ 50 - 0
docs/src/cmd/ansi.md

@@ -0,0 +1,50 @@
+# 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"));
+}
+```
+

+ 222 - 0
docs/src/cmd/awesome.md

@@ -0,0 +1,222 @@
+## 命令行工具
+对于每一个程序员而言,命令行工具都非常关键。你对他越熟悉,在使用计算机、处理工作流程等越是高效。
+
+下面我们收集了一些优秀的Rust所写的命令行工具,它们相比目前已有的其它语言的实现,可以提供更加现代化的代码实现、更加高效的性能以及更好的可用性。
+
+### 索引目录
+| 新工具 | 替代的目标或功能描述 | 
+| ------ | ----------------- |
+| [bat](#bat) | cat |
+| [exa](#exa) | ls |
+| [lsd](#lsd) | ls |
+| [fd](#fd) | find |
+| [procs](#procs) | ps | 
+| [sd](#sd) | sed |
+| [dust](#dust) | du |
+| [starship](#starship) | 现代化的命令行提示 | 
+| [ripgrep](#ripgrep) | grep |
+| [tokei](#tokei) | 代码统计工具 | 
+| [hyperfine](#hyperfine) | 命令行benchmark工具 | 
+| [bottom](#bottom) | top |
+| [teeldear](#tealdear) | tldr |
+| [grex](#grex) | 根据文本示例生成正则 | 
+| [bandwitch](#bandwhich) | 显示进程、连接网络使用情况|
+| [zoxide](#zoxide) | cd |
+| [delta](#delta) | git可视化 |
+| [nushell](#nushell) | 全新的现代化shell |
+| [mcfly](#mcfly) | 替代`ctrl + R`命令搜索 | 
+| [fselect](#fselect) | 使用SQL语法查找文件 |
+| [pueue](#pueue) | 命令行任务管理工具 | 
+| [watchexec](#watchexec) | 监视目录文件变动并执行命令 | 
+| [dura](#dura) | 更加安全的使用git | 
+| [alacritty](#alacritty) | 强大的基于OpenGL的终端 | 
+| [broot](#broot) | 可视化访问目录树 | 
+
+### bat
+[bat](https://github.com/sharkdp/bat)`克隆了**cat**的功能并提供了语法高亮和Git集成,它支持`Windows`,`MacOS`和`Linux`。同时,它默认提供了多种文件后缀的语法高亮。
+
+<img alt="bat screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/command-line/bat.png?raw=true" class="center"  />
+
+
+
+### exa
+[exa](https://github.com/ogham/exa)是`ls`命令的现代化实现,后者是目前`Unix/Linux`系统的默认命令,用于列出当前目录中的内容。
+
+<img alt="exa screenshot" width="100%"  src="https://github.com/studyrs/cookbook-images/blob/main/command-line/exa.jpg?raw=true" class="center"  />
+
+### lsd
+[lsd](https://github.com/Peltoche/lsd) 也是 `ls` 的新实现,同时增加了很多特性,例如:颜色标注、icons、树形查看、更多的格式化选项等。
+
+<img alt="lsd screenshot" width="100%"  src="https://github.com/studyrs/cookbook-images/blob/main/command-line/lsd.png?raw=true" class="center"  />
+
+### fd
+[fd](https://github.com/sharkdp/fd) 是一个更快、对用户更友好的**find**实现,后者是 `Unix/Linux` 内置的文件目录搜索工具。之所以说它用户友好,一方面是 `API` 非常清晰明了,其次是它对最常用的场景提供了有意义的默认值:例如,想要通过名称搜索文件:
+
+- `fd`: `fd PATTERN`
+- `find`: `find -iname 'PATTERN'`
+
+同时 `fd` 性能非常非常高,还提供了非常多的搜索选项,例如允许用户通过 `.gitignore` 文件忽略隐藏的目录、文件等。
+
+<img alt="fd screenshot" width="100%"  src="https://github.com/studyrs/cookbook-images/blob/main/command-line/fd.svg?raw=true" class="center"  />
+
+### procs
+[procs](https://github.com/dalance/procs) 是 **ps** 的默认实现,后者是 `Unix/Linux` 的内置命令,用于获取进程( `process` )的信息。`proc` 提供了更便利、可读性更好的格式化输出。
+
+<img alt="procs screenshot" width="100%"  src="https://github.com/studyrs/cookbook-images/blob/main/command-line/procs.jpg?raw=true" class="center"  />
+
+ ### sd
+[sd](https://github.com/chmln/sd) 是 **sed** 命令的现代化实现,后者是 `Unix/Linux` 中内置的工具,用于分析和转换文本。
+
+ `sd` 拥有更简单的使用方式,而且支持方便的正则表达式语法,`sd` 拥有闪电般的性能,比 `sed` 快 **2x-11x** 倍。
+
+以下是其中一个性能测试结果:
+
+*对1.5G大小的 JSON 文本进行简单替换*
+
+`hyperfine -w 3 'sed -E "s/\"/\'/g" *.json >/dev/null' 'sd "\"" "\'" *.json >/dev/null' --export-markdown out.md`
+
+| Command | Mean [s] | Min…Max [s] |
+|:---|---:|---:|
+| `sed -E "s/\"/'/g" *.json >/dev/null` | 2.338 ± 0.008 | 2.332…2.358 |
+| `sed "s/\"/'/g" *.json >/dev/null` | 2.365 ± 0.009 | 2.351…2.378 |
+| `sd "\"" "'" *.json >/dev/null` | **0.997 ± 0.006** | 0.987…1.007 |
+
+结果: ~2.35 times faster
+
+### dust
+[dust](https://github.com/bootandy/dust) 是一个更符合使用习惯的**du**,后者是 `Unix/Linux` 内置的命令行工具,用于显示硬盘使用情况的统计。
+
+<img alt="dust screenshot" width="100%"  src="https://github.com/studyrs/cookbook-images/blob/main/command-line/dust.png?raw=true" class="center"  />
+
+### starship
+[starship](https://github.com/starship/starship) 是一个命令行提示,支持任何 `shell` ,包括 `zsh` ,简单易用、非常快且拥有极高的可配置性, 同时支持智能提示。
+
+<img alt="starship screenshot" width="100%"  src="https://raw.githubusercontent.com/starship/starship/master/media/demo.gif" class="center"  />
+
+### [ripgrep](https://github.com/BurntSushi/ripgrep)
+[ripgrep](https://github.com/BurntSushi/ripgrep) 是一个性能极高的现代化 `grep` 实现,后者是 `Unix/Linux` 下的内置文件搜索工具。该项目是 Rust 的明星项目,一个是因为性能极其的高,另一个就是源代码质量很高,值得学习, 同时 `Vscode` 使用它作为内置的搜索引擎。
+
+从功能来说,除了全面支持 `grep` 的功能外,`repgre` 支持使用正则递归搜索指定的文件目录,默认使用 `.gitignore` 对指定的文件进行忽略。
+
+<img alt="ripgrep screenshot" width="100%"  src="https://github.com/studyrs/cookbook-images/blob/main/command-line/ripgrep.png?raw=true" class="center"  />
+
+
+### tokei
+[tokei](https://github.com/XAMPPRocky/tokei) 可以分门别类的统计目录内的代码行数,速度非常快!
+
+<img alt="tokei screenshot" width="100%"  src="https://github.com/studyrs/cookbook-images/blob/main/command-line/tokei.png?raw=true" class="center"  />
+
+### hyperfine
+[hyperfine](https://github.com/sharkdp/hyperfine) 是命令行benchmark工具,它支持在多次运行中提供静态的分析,同时支持任何的 `shell` 命令,准确的 `benchmark` 进度和当前预估等等高级特性。
+
+
+<img alt="hyperfine screenshot" width="100%"  src="https://github.com/studyrs/cookbook-images/blob/main/command-line/hyperfine.gif?raw=true" class="center"  />
+
+### bottom
+[bottom](https://github.com/ClementTsang/bottom) 是一个现代化实现的 `top`,可以跨平台、图形化的显示进程/系统的当前信息。
+
+<img alt="bottom screenshot" width="100%"  src="https://github.com/ClementTsang/bottom/raw/master/assets/demo.gif" class="center"  />
+
+### tealdear
+[tealdear](https://github.com/dbrgn/tealdeer) 是一个更快实现的**tldr**, 一个用于显示 `man pages` 的命令行程序,简单易用、基于例子和社区驱动是主要特性。
+
+<img alt="teeldear screenshot" width="100%"  src="https://github.com/studyrs/cookbook-images/blob/main/command-line/teeldear.gif?raw=true" class="center"  />
+
+### bandwhich
+[bandwhich](https://github.com/imsnif/bandwhich) 是一个客户端实用工具,用于显示当前进程、连接、远程 IP( hostname ) 的网络信息。
+
+<img alt="bandwhich screenshot" width="100%"  src="https://github.com/studyrs/cookbook-images/blob/main/command-line/bandwhich.gif?raw=true" class="center"  />
+
+### grex
+[grex](https://github.com/pemistahl/grex) 既是一个命令行工具又是一个库,可以根据用户提供的文本示例生成对应的正则表达式,非常强大。
+
+<img alt="grex screenshot" width="100%"  src="https://github.com/studyrs/cookbook-images/blob/main/command-line/grex.gif?raw=true" class="center"  />
+
+### zoxide
+[zoxide](https://github.com/ajeetdsouza/zoxide) 是一个智能化的 `cd` 命令,它甚至会记忆你常用的目录。
+
+<img alt="zoxide screenshot" width="100%"  src="https://github.com/studyrs/cookbook-images/blob/main/command-line/zoxide.webp?raw=true" class="center"  />
+
+### delta
+[delta](https://github.com/dandavison/delta) 是一个 `git` 分页展示工具,支持语法高亮、代码比对、输出 `grep` 等。
+
+
+<img alt="delta screenshot" width="100%"  src="https://github.com/studyrs/cookbook-images/blob/main/command-line/delta.png?raw=true" class="center"  />
+
+### nushell
+[nushell](https://github.com/nushell/nushell) 是一个全新的 `shell` ,使用 `Rust` 实现。它的目标是创建一个现代化的 `shell` :虽然依然基于 `Unix` 的哲学,但是更适合现在的时代。例如,你可以使用 `SQL` 语法来选择你想要的内容!
+
+<img alt="delta screenshot" width="100%"  src="https://github.com/nushell/nushell/raw/main/images/nushell-autocomplete5.gif" class="center"  />
+
+### mcfly
+[mcfly](https://github.com/cantino/mcfly) 会替换默认的 `ctrl-R`,用于在终端中搜索历史命令, 它提供了智能提示功能,并且会根据当前目录中最近执行过的上下文命令进行提示。`mcfly` 甚至使用了一个小型的神经网络用于智能提示!
+
+<img alt="mcfly screenshot" width="100%"  src="https://github.com/studyrs/cookbook-images/blob/main/command-line/mcfly.png?raw=true" class="center"  />
+
+### fselect
+[fselect](https://github.com/jhspetersson/fselect) 允许使用 SQL 语法来查找系统中的文件。它支持复杂查询、聚合查询、.gitignore 忽略文件、通过宽度高度搜索图片、通过 hash 搜索文件、文件属性查询等等,相当强大!
+
+```shell
+# 复杂查询
+fselect "name from /tmp where (name = *.tmp and size = 0) or (name = *.cfg and size > 1000000)"
+
+# 聚合函数
+fselect "MIN(size), MAX(size), AVG(size), SUM(size), COUNT(*) from /home/user/Downloads"
+
+# 格式化函数
+fselect "LOWER(name), UPPER(name), LENGTH(name), YEAR(modified) from /home/user/Downloads"
+```
+
+### pueue
+[pueue](https://github.com/nukesor/pueue) 是一个命令行任务管理工具,它可以管理你的长时间运行的命令,支持顺序或并行执行。简单来说,它可以管理一个命令队列。
+
+<img alt="pueue screenshot" width="100%"  src="https://github.com/studyrs/cookbook-images/blob/main/command-line/pueue.gif?raw=true" class="center"  />
+
+### watchexec
+[watchexec](https://github.com/watchexec/watchexec) 可以监视指定的目录、文件的改动,并执行你预设的命令,支持多种配置项和操作系统。
+
+```shell
+# 监视当前目录/子目录中的所有js、css、html文件,一旦发生改变,运行`npm run build`命令
+$ watchexec -e js,css,html npm run build
+
+# 当前目录/子目录下任何python文件发生改变时,重启`python server.py`
+$ watchexec -r -e py -- python server.py
+```
+
+### dura
+[dura](https://github.com/tkellogg/dura) 运行在后台,监视你的 `git` 目录,提交你未提交的更改但是并不会影响 `HEAD`、当前的分支和 `git` 索引(staged文件)。
+
+如果你曾经遇到过**"完蛋, 我这几天的工作内容丢了"**的情况,那么就可以尝试下 `dura`,`checkout dura brach`,然后代码就可以顺利恢复了:)
+
+**恢复代码**
+1. 你可以使用 `dura` 分支来恢复
+
+```console
+$ echo "dura/$(git rev-parse HEAD)"
+```
+
+1. 也可以手动恢复
+
+```console
+# Or, if you don't trust dura yet, `git stash`
+$ git reset HEAD --hard
+# get the changes into your working directory
+$ git checkout $THE_HASH
+# last few commands reset HEAD back to master but with changes uncommitted
+$ git checkout -b temp-branch
+$ git reset master
+$ git checkout master
+$ git branch -D temp-branch
+```
+
+## alacritty 
+[alacritty](https://github.com/alacritty/alacritty) 是一个跨平台、基于OpenGL的终端,性能极高的同时还支持丰富的自定义和可扩展性,可以说是非常优秀的现代化终端。
+
+目前已经是 `beta` 阶段,可以作为日常工具来使用。
+
+<img alt="alacritty screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/superstar/alacritty.png?raw=true" class="center"  />
+
+## broot
+[`broot`](https://github.com/Canop/broot) 允许你可视化的去访问一个目录结构。
+
+<img alt="broot screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/command-line/broot.png?raw=true" class="center"  />

+ 70 - 0
docs/src/cmd/parsing.md

@@ -0,0 +1,70 @@
+# 参数解析
+
+## 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

+ 201 - 0
docs/src/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)?)
+}
+```

+ 336 - 0
docs/src/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(())
+}
+```

+ 123 - 0
docs/src/database/postgres.md

@@ -0,0 +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(())
+}
+```
+

+ 136 - 0
docs/src/database/sqlite.md

@@ -0,0 +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()
+}
+```

+ 69 - 0
docs/src/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));
+}
+```

+ 114 - 0
docs/src/datetime/parsing.md

@@ -0,0 +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(())
+}
+```

+ 1 - 0
docs/src/dev/intro.md

@@ -0,0 +1 @@
+# 开发调试

+ 1 - 0
docs/src/dev/logs.md

@@ -0,0 +1 @@
+# 日志

+ 1 - 0
docs/src/dev/profile.md

@@ -0,0 +1 @@
+# 性能分析

+ 192 - 0
docs/src/devtools/build-tools.md

@@ -0,0 +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();
+    }   
+}
+```
+

+ 140 - 0
docs/src/devtools/config-log.md

@@ -0,0 +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(())
+}
+
+```

+ 127 - 0
docs/src/devtools/log.md

@@ -0,0 +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

+ 186 - 0
docs/src/devtools/version.md

@@ -0,0 +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(())
+}
+```

+ 277 - 0
docs/src/encoding/csv.md

@@ -0,0 +1,277 @@
+# CSV
+
+## 读取 CSV 记录
+我们可以将标准的 CSV 记录值读取到 [csv::StringRecord](https://docs.rs/csv/*/csv/struct.StringRecord.html) 中,但是该数据结构期待合法的 UTF8 数据行,你还可以使用 [csv::ByteRecord](https://docs.rs/csv/1.1.6/csv/struct.ByteRecord.html) 来读取非 UTF8 数据。
+
+```rust,editable
+use csv::Error;
+
+fn main() -> Result<(), Error> {
+    let csv = "year,make,model,description
+        1948,Porsche,356,Luxury sports car
+        1967,Ford,Mustang fastback 1967,American car";
+
+    let mut reader = csv::Reader::from_reader(csv.as_bytes());
+    for record in reader.records() {
+        let record = record?;
+        println!(
+            "In {}, {} built the {} model. It is a {}.",
+            &record[0],
+            &record[1],
+            &record[2],
+            &record[3]
+        );
+    }
+
+    Ok(())
+}
+```
+
+还可以使用 [`serde`](https://docs.rs/serde/1.0.136/serde/) 将数据反序列化成一个强类型的结构体。
+
+```rust,editable
+use serde::Deserialize;
+#[derive(Deserialize)]
+struct Record {
+    year: u16,
+    make: String,
+    model: String,
+    description: String,
+}
+
+fn main() -> Result<(), csv::Error> {
+    let csv = "year,make,model,description
+1948,Porsche,356,Luxury sports car
+1967,Ford,Mustang fastback 1967,American car";
+
+    let mut reader = csv::Reader::from_reader(csv.as_bytes());
+
+    for record in reader.deserialize() {
+        let record: Record = record?;
+        println!(
+            "In {}, {} built the {} model. It is a {}.",
+            record.year,
+            record.make,
+            record.model,
+            record.description
+        );
+    }
+
+    Ok(())
+}
+```
+
+### 读取使用了不同分隔符的 CSV 记录
+下面的例子将读取使用了 `tab` 作为分隔符的 CSV 记录。
+
+```rust,editable
+use csv::Error;
+use serde::Deserialize;
+#[derive(Debug, Deserialize)]
+struct Record {
+    name: String,
+    place: String,
+    #[serde(deserialize_with = "csv::invalid_option")]
+    id: Option<u64>,
+}
+
+use csv::ReaderBuilder;
+
+fn main() -> Result<(), Error> {
+    let data = "name\tplace\tid
+        Mark\tMelbourne\t46
+        Ashley\tZurich\t92";
+
+    let mut reader = ReaderBuilder::new().delimiter(b'\t').from_reader(data.as_bytes());
+    for result in reader.deserialize::<Record>() {
+        println!("{:?}", result?);
+    }
+
+    Ok(())
+}
+```
+
+### 基于给定条件来过滤 CSV 记录
+
+```rust,editable
+use error_chain::error_chain;
+
+use std::io;
+
+error_chain!{
+    foreign_links {
+        Io(std::io::Error);
+        CsvError(csv::Error);
+    }
+}
+
+fn main() -> Result<()> {
+    let query = "CA";
+    let data = "\
+City,State,Population,Latitude,Longitude
+Kenai,AK,7610,60.5544444,-151.2583333
+Oakman,AL,,33.7133333,-87.3886111
+Sandfort,AL,,32.3380556,-85.2233333
+West Hollywood,CA,37031,34.0900000,-118.3608333";
+
+    let mut rdr = csv::ReaderBuilder::new().from_reader(data.as_bytes());
+    let mut wtr = csv::Writer::from_writer(io::stdout());
+
+    wtr.write_record(rdr.headers()?)?;
+
+    for result in rdr.records() {
+        let record = result?;
+        if record.iter().any(|field| field == query) {
+            wtr.write_record(&record)?;
+        }
+    }
+
+    wtr.flush()?;
+    Ok(())
+}
+```
+
+### 序列化为 CSV
+
+下面例子展示了如何将 Rust 类型序列化为 CSV。
+
+```rust,editable
+use std::io;
+
+fn main() -> Result<()> {
+    let mut wtr = csv::Writer::from_writer(io::stdout());
+
+    wtr.write_record(&["Name", "Place", "ID"])?;
+
+    wtr.serialize(("Mark", "Sydney", 87))?;
+    wtr.serialize(("Ashley", "Dublin", 32))?;
+    wtr.serialize(("Akshat", "Delhi", 11))?;
+
+    wtr.flush()?;
+    Ok(())
+}
+```
+
+### 使用 serde 序列化为 CSV
+下面例子将自定义数据结构通过 `serde` 序列化 CSV。
+```rust,editable
+use error_chain::error_chain;
+use serde::Serialize;
+use std::io;
+
+error_chain! {
+   foreign_links {
+       IOError(std::io::Error);
+       CSVError(csv::Error);
+   }
+}
+
+#[derive(Serialize)]
+struct Record<'a> {
+    name: &'a str,
+    place: &'a str,
+    id: u64,
+}
+
+fn main() -> Result<()> {
+    let mut wtr = csv::Writer::from_writer(io::stdout());
+
+    let rec1 = Record { name: "Mark", place: "Melbourne", id: 56};
+    let rec2 = Record { name: "Ashley", place: "Sydney", id: 64};
+    let rec3 = Record { name: "Akshat", place: "Delhi", id: 98};
+
+    wtr.serialize(rec1)?;
+    wtr.serialize(rec2)?;
+    wtr.serialize(rec3)?;
+
+    wtr.flush()?;
+
+    Ok(())
+}
+```
+
+### CSV 列转换
+下面代码将包含有颜色名和十六进制颜色的 CSV 文件转换为包含颜色名和 rgb 颜色。这里使用 `csv` 包对 CSV 文件进行读写,然后用 `serde` 进行序列化和反序列化。
+
+```rust,editable
+#use error_chain::error_chain;
+use csv::{Reader, Writer};
+use serde::{de, Deserialize, Deserializer};
+use std::str::FromStr;
+
+#error_chain! {
+#   foreign_links {
+#       CsvError(csv::Error);
+#       ParseInt(std::num::ParseIntError);
+#       CsvInnerError(csv::IntoInnerError<Writer<Vec<u8>>>);
+#       IO(std::fmt::Error);
+#       UTF8(std::string::FromUtf8Error);
+#   }
+#}
+
+#[derive(Debug)]
+struct HexColor {
+    red: u8,
+    green: u8,
+    blue: u8,
+}
+
+#[derive(Debug, Deserialize)]
+struct Row {
+    color_name: String,
+    color: HexColor,
+}
+
+impl FromStr for HexColor {
+    type Err = Error;
+
+    fn from_str(hex_color: &str) -> std::result::Result<Self, Self::Err> {
+        let trimmed = hex_color.trim_matches('#');
+        if trimmed.len() != 6 {
+            Err("Invalid length of hex string".into())
+        } else {
+            Ok(HexColor {
+                red: u8::from_str_radix(&trimmed[..2], 16)?,
+                green: u8::from_str_radix(&trimmed[2..4], 16)?,
+                blue: u8::from_str_radix(&trimmed[4..6], 16)?,
+            })
+        }
+    }
+}
+
+impl<'de> Deserialize<'de> for HexColor {
+    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        let s = String::deserialize(deserializer)?;
+        FromStr::from_str(&s).map_err(de::Error::custom)
+    }
+}
+
+fn main() -> Result<()> {
+    let data = "color_name,color
+red,#ff0000
+green,#00ff00
+blue,#0000FF
+periwinkle,#ccccff
+magenta,#ff00ff"
+        .to_owned();
+    let mut out = Writer::from_writer(vec![]);
+    let mut reader = Reader::from_reader(data.as_bytes());
+    for result in reader.deserialize::<Row>() {
+        let res = result?;
+        out.serialize((
+            res.color_name,
+            res.color.red,
+            res.color.green,
+            res.color.blue,
+        ))?;
+    }
+    let written = String::from_utf8(out.into_inner()?)?;
+    assert_eq!(Some("magenta,255,0,255"), written.lines().last());
+    println!("{}", written);
+    Ok(())
+}
+```
+

+ 1 - 0
docs/src/encoding/intro.md

@@ -0,0 +1 @@
+# 编解码

+ 1 - 0
docs/src/encoding/json.md

@@ -0,0 +1 @@
+# JSON

+ 1 - 0
docs/src/encoding/protobuf.md

@@ -0,0 +1 @@
+# protobuf

+ 106 - 0
docs/src/encoding/strings.md

@@ -0,0 +1,106 @@
+# 字符编码
+
+### 百分号编码( Percent encoding )
+[百分号编码](https://en.wikipedia.org/wiki/Percent-encoding)又称 URL 编码。
+
+[percent-encoding](https://docs.rs/crate/percent-encoding/2.1.0) 包提供了两个函数:`utf8_percent_encode` 函数用于编码、`percent_decode` 用于解码。
+
+```rust,editable
+use percent_encoding::{utf8_percent_encode, percent_decode, AsciiSet, CONTROLS};
+use std::str::Utf8Error;
+
+/// https://url.spec.whatwg.org/#fragment-percent-encode-set
+const FRAGMENT: &AsciiSet = &CONTROLS.add(b' ').add(b'"').add(b'<').add(b'>').add(b'`');
+
+fn main() -> Result<(), Utf8Error> {
+    let input = "confident, productive systems programming";
+
+    let iter = utf8_percent_encode(input, FRAGMENT);
+    // 将元素类型为 &str 的迭代器收集为 String 类型
+    let encoded: String = iter.collect();
+    assert_eq!(encoded, "confident,%20productive%20systems%20programming");
+
+    let iter = percent_decode(encoded.as_bytes());
+    let decoded = iter.decode_utf8()?;
+    assert_eq!(decoded, "confident, productive systems programming");
+
+    Ok(())
+}
+```
+
+该编码集定义了哪些字符( 特别是非 ASCII 和控制字符 )需要被百分比编码。具体的选择取决于上下文,例如 `url` 会对 URL 路径中的 `?` 进行编码,但是在路径后的查询字符串中,并不会进行编码。
+
+
+### 将字符串编码为 application/x-www-form-urlencoded
+
+使用 [form_urlencoded::byte_serialize](https://docs.rs/form_urlencoded/1.0.1/form_urlencoded/fn.byte_serialize.html) 函数将一个字符串编码成 [application/x-www-form-urlencoded](https://url.spec.whatwg.org/#application/x-www-form-urlencoded) 格式,然后再使用 [form_urlencoded::parse](https://docs.rs/form_urlencoded/1.0.1/form_urlencoded/fn.parse.html) 对其进行解码。
+
+```rust,editable
+use url::form_urlencoded::{byte_serialize, parse};
+
+fn main() {
+    let urlencoded: String = byte_serialize("What is ❤?".as_bytes()).collect();
+    assert_eq!(urlencoded, "What+is+%E2%9D%A4%3F");
+    println!("urlencoded:'{}'", urlencoded);
+
+    let decoded: String = parse(urlencoded.as_bytes())
+        .map(|(key, val)| [key, val].concat())
+        .collect();
+    assert_eq!(decoded, "What is ❤?");
+    println!("decoded:'{}'", decoded);
+}
+```
+
+### 十六进制编解码
+
+[data_encoding](https://docs.rs/data-encoding/*/data_encoding/) 可以将一个字符串编码成十六进制字符串,反之亦然。
+
+下面的例子将 `&[u8]` 转换成十六进制等效形式,然后与期待的值进行比较。
+
+```rust,editable
+use data_encoding::{HEXUPPER, DecodeError};
+
+fn main() -> Result<(), DecodeError> {
+    let original = b"The quick brown fox jumps over the lazy dog.";
+    let expected = "54686520717569636B2062726F776E20666F78206A756D7073206F76\
+        657220746865206C617A7920646F672E";
+
+    let encoded = HEXUPPER.encode(original);
+    assert_eq!(encoded, expected);
+
+    let decoded = HEXUPPER.decode(&encoded.into_bytes())?;
+    assert_eq!(&decoded[..], &original[..]);
+
+    Ok(())
+}
+```
+
+### Base64 编解码
+[base64](https://docs.rs/base64/0.13.0/base64/index.html) 可以把一个字节切片编码成 `base64` String。
+
+```rust,editable
+use error_chain::error_chain;
+
+use std::str;
+use base64::{encode, decode};
+
+error_chain! {
+    foreign_links {
+        Base64(base64::DecodeError);
+        Utf8Error(str::Utf8Error);
+    }
+}
+
+fn main() -> Result<()> {
+    // 将 `&str` 转换成 `&[u8; N]` 
+    let hello = b"hello rustaceans";
+    let encoded = encode(hello);
+    let decoded = decode(&encoded)?;
+
+    println!("origin: {}", str::from_utf8(hello)?);
+    println!("base64 encoded: {}", encoded);
+    println!("back to origin: {}", str::from_utf8(&decoded)?);
+
+    Ok(())
+}
+```

+ 142 - 0
docs/src/encoding/structured.md

@@ -0,0 +1,142 @@
+# 结构化数据
+
+### 序列和反序列非结构化的JSON
+[serde_json]() 是一个高性能的 JSON 包,它支持我们在不声明结构体的情况下,去解析 JSON。
+
+```rust,editable
+use serde_json::json;
+use serde_json::{Value, Error};
+
+fn main() -> Result<(), Error> {
+    let j = r#"{
+                 "userid": 103609,
+                 "verified": true,
+                 "access_privileges": [
+                   "user",
+                   "admin"
+                 ]
+               }"#;
+
+    let parsed: Value = serde_json::from_str(j)?;
+
+    let expected = json!({
+        "userid": 103609,
+        "verified": true,
+        "access_privileges": [
+            "user",
+            "admin"
+        ]
+    });
+
+    assert_eq!(parsed, expected);
+
+    Ok(())
+}
+```
+
+### 解析 TOML 文件
+[toml](https://docs.rs/toml/latest/toml/) 包可以将 TOML 文件的内容解析为一个 `toml::Value` 值,该值能代表任何合法的 TOML 数据。
+
+```rust,editable
+use toml::{Value, de::Error};
+
+fn main() -> Result<(), Error> {
+    let toml_content = r#"
+          [package]
+          name = "your_package"
+          version = "0.1.0"
+          authors = ["You! <you@example.org>"]
+
+          [dependencies]
+          serde = "1.0"
+          "#;
+
+    let package_info: Value = toml::from_str(toml_content)?;
+
+    assert_eq!(package_info["dependencies"]["serde"].as_str(), Some("1.0"));
+    assert_eq!(package_info["package"]["name"].as_str(),
+               Some("your_package"));
+
+    Ok(())
+}
+```
+
+还可以配合 [serde]() 将 TOML 解析到我们自定义的结构体中:
+```rust,editable
+use serde::Deserialize;
+
+use toml::de::Error;
+use std::collections::HashMap;
+
+#[derive(Deserialize)]
+struct Config {
+    package: Package,
+    dependencies: HashMap<String, String>,
+}
+
+#[derive(Deserialize)]
+struct Package {
+    name: String,
+    version: String,
+    authors: Vec<String>,
+}
+
+fn main() -> Result<(), Error> {
+    let toml_content = r#"
+          [package]
+          name = "your_package"
+          version = "0.1.0"
+          authors = ["You! <you@example.org>"]
+
+          [dependencies]
+          serde = "1.0"
+          "#;
+
+    let package_info: Config = toml::from_str(toml_content)?;
+
+    assert_eq!(package_info.package.name, "your_package");
+    assert_eq!(package_info.package.version, "0.1.0");
+    assert_eq!(package_info.package.authors, vec!["You! <you@example.org>"]);
+    assert_eq!(package_info.dependencies["serde"], "1.0");
+
+    Ok(())
+}
+```
+
+### 使用小端字节序来读写整数
+[byteorder](https://docs.rs/byteorder/latest/byteorder/) 在自行接收或发送网络字节流时会非常有用( 除非性能要求高,否则还是建议使用 JSON 等数据协议,不要自己做字节流解析 )。
+
+```rust,editable
+
+use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
+use std::io::Error;
+
+#[derive(Default, PartialEq, Debug)]
+struct Payload {
+    kind: u8,
+    value: u16,
+}
+
+fn main() -> Result<(), Error> {
+    let original_payload = Payload::default();
+    let encoded_bytes = encode(&original_payload)?;
+    let decoded_payload = decode(&encoded_bytes)?;
+    assert_eq!(original_payload, decoded_payload);
+    Ok(())
+}
+
+fn encode(payload: &Payload) -> Result<Vec<u8>, Error> {
+    let mut bytes = vec![];
+    bytes.write_u8(payload.kind)?;
+    bytes.write_u16::<LittleEndian>(payload.value)?;
+    Ok(bytes)
+}
+
+fn decode(mut bytes: &[u8]) -> Result<Payload, Error> {
+    let payload = Payload {
+        kind: bytes.read_u8()?,
+        value: bytes.read_u16::<LittleEndian>()?,
+    };
+    Ok(payload)
+}
+```

+ 242 - 0
docs/src/files/dir.md

@@ -0,0 +1,242 @@
+# 目录访问
+
+### 获取24小时内被修改过的文件
+通过遍历读取目录中文件的 [Metadata::modified](https://doc.rust-lang.org/std/fs/struct.Metadata.html#method.modified) 属性,来获取目标文件名列表。
+
+```rust,editable
+use error_chain::error_chain;
+
+use std::{env, fs};
+
+error_chain! {
+    foreign_links {
+        Io(std::io::Error);
+        SystemTimeError(std::time::SystemTimeError);
+    }
+}
+
+fn main() -> Result<()> {
+    let current_dir = env::current_dir()?;
+    println!(
+        "Entries modified in the last 24 hours in {:?}:",
+        current_dir
+    );
+
+    for entry in fs::read_dir(current_dir)? {
+        let entry = entry?;
+        let path = entry.path();
+
+        let metadata = fs::metadata(&path)?;
+        let last_modified = metadata.modified()?.elapsed()?.as_secs();
+
+        if last_modified < 24 * 3600 && metadata.is_file() {
+            println!(
+                "Last modified: {:?} seconds, is read only: {:?}, size: {:?} bytes, filename: {:?}",
+                last_modified,
+                metadata.permissions().readonly(),
+                metadata.len(),
+                path.file_name().ok_or("No filename")?
+            );
+        }
+    }
+
+    Ok(())
+}
+```
+
+### 获取给定路径的 loops
+
+使用 [same_file::is_same_file]() 可以检查给定路径的 loops,loop 可以通过以下方式创建:
+```shell
+mkdir -p /tmp/foo/bar/baz
+ln -s /tmp/foo/  /tmp/foo/bar/baz/qux
+```
+
+```rust,editable
+use std::io;
+use std::path::{Path, PathBuf};
+use same_file::is_same_file;
+
+fn contains_loop<P: AsRef<Path>>(path: P) -> io::Result<Option<(PathBuf, PathBuf)>> {
+    let path = path.as_ref();
+    let mut path_buf = path.to_path_buf();
+    while path_buf.pop() {
+        if is_same_file(&path_buf, path)? {
+            return Ok(Some((path_buf, path.to_path_buf())));
+        } else if let Some(looped_paths) = contains_loop(&path_buf)? {
+            return Ok(Some(looped_paths));
+        }
+    }
+    return Ok(None);
+}
+
+fn main() {
+    assert_eq!(
+        contains_loop("/tmp/foo/bar/baz/qux/bar/baz").unwrap(),
+        Some((
+            PathBuf::from("/tmp/foo"),
+            PathBuf::from("/tmp/foo/bar/baz/qux")
+        ))
+    );
+}
+```
+
+### 递归查找重复的文件名
+
+[walkdir](https://docs.rs/walkdir/latest/walkdir/) 可以帮助我们遍历指定的目录。
+
+```rust,editable
+use std::collections::HashMap;
+use walkdir::WalkDir;
+
+fn main() {
+    let mut filenames = HashMap::new();
+
+    // 遍历当前目录
+    for entry in WalkDir::new(".")
+            .into_iter()
+            .filter_map(Result::ok)
+            .filter(|e| !e.file_type().is_dir()) {
+        let f_name = String::from(entry.file_name().to_string_lossy());
+        let counter = filenames.entry(f_name.clone()).or_insert(0);
+        *counter += 1;
+
+        if *counter == 2 {
+            println!("{}", f_name);
+        }
+    }
+}
+```
+
+### 递归查找满足条件的所有文件
+
+下面的代码通过 [walkdir](https://docs.rs/walkdir/latest/walkdir/) 来查找当前目录中最近一天内发生过修改的所有文件。
+
+[follow_links](https://docs.rs/walkdir/*/walkdir/struct.WalkDir.html#method.follow_links) 为 `true` 时,那软链接会被当成正常的文件或目录一样对待,也就是说软链接指向的文件或目录也会被访问和检查。若软链接指向的目标不存在或它是一个 loops,就会导致错误的发生。
+
+```rust,editable
+#use error_chain::error_chain;
+
+use walkdir::WalkDir;
+
+#error_chain! {
+#    foreign_links {
+#        WalkDir(walkdir::Error);
+#        Io(std::io::Error);
+#        SystemTime(std::time::SystemTimeError);
+#    }
+#}
+
+fn main() -> Result<()> {
+    for entry in WalkDir::new(".")
+            .follow_links(true)
+            .into_iter()
+            .filter_map(|e| e.ok()) {
+        let f_name = entry.file_name().to_string_lossy();
+        let sec = entry.metadata()?.modified()?;
+
+        if f_name.ends_with(".json") && sec.elapsed()?.as_secs() < 86400 {
+            println!("{}", f_name);
+        }
+    }
+
+    Ok(())
+}
+```
+
+### 遍历目录跳过隐藏文件
+
+下面例子使用 [walkdir](https://docs.rs/walkdir/latest/walkdir/) 来遍历一个目录,同时跳过隐藏文件 `is_not_hidden`。
+
+```rust,editable
+use walkdir::{DirEntry, WalkDir};
+
+fn is_not_hidden(entry: &DirEntry) -> bool {
+    entry
+         .file_name()
+         .to_str()
+         .map(|s| entry.depth() == 0 || !s.starts_with("."))
+         .unwrap_or(false)
+}
+
+fn main() {
+    WalkDir::new(".")
+        .into_iter()
+        .filter_entry(|e| is_not_hidden(e))
+        .filter_map(|v| v.ok())
+        .for_each(|x| println!("{}", x.path().display()));
+}
+```
+
+### 递归计算给定深度的文件大小
+递归访问的深度可以使用 [WalkDir::min_depth](https://docs.rs/walkdir/*/walkdir/struct.WalkDir.html#method.min_depth) 和 [WalkDir::max_depth](https://docs.rs/walkdir/2.3.2/walkdir/struct.WalkDir.html#method.max_depth) 来控制。
+
+```rust,editable
+use walkdir::WalkDir;
+
+fn main() {
+    let total_size = WalkDir::new(".")
+        .min_depth(1)
+        .max_depth(3)
+        .into_iter()
+        .filter_map(|entry| entry.ok())
+        .filter_map(|entry| entry.metadata().ok())
+        .filter(|metadata| metadata.is_file())
+        .fold(0, |acc, m| acc + m.len());
+
+    println!("Total size: {} bytes.", total_size);
+}
+```
+
+### 递归查找所有 png 文件
+例子中使用了 [glob](https://docs.rs/glob/) 包,其中的 `**` 代表当前目录及其所有子目录,例如,`/media/**/*.png` 代表在 `media` 和它的所有子目录下查找 png 文件.
+
+```rust,editable
+#use error_chain::error_chain;
+
+use glob::glob;
+
+#error_chain! {
+#    foreign_links {
+#        Glob(glob::GlobError);
+#        Pattern(glob::PatternError);
+#    }
+#}
+
+fn main() -> Result<()> {
+    for entry in glob("**/*.png")? {
+        println!("{}", entry?.display());
+    }
+
+    Ok(())
+}
+```
+
+### 查找满足给定正则的所有文件且忽略文件名大小写
+
+[glob_with](https://docs.rs/glob/*/glob/fn.glob_with.html) 函数可以按照给定的正则表达式进行查找,同时还能使用选项来控制一些匹配设置。
+
+```rust,editable
+use error_chain::error_chain;
+use glob::{glob_with, MatchOptions};
+
+error_chain! {
+    foreign_links {
+        Glob(glob::GlobError);
+        Pattern(glob::PatternError);
+    }
+}
+
+fn main() -> Result<()> {
+    let options = MatchOptions {
+        case_sensitive: false,
+        ..Default::default()
+    };
+
+    for entry in glob_with("/media/img_[0-9]*.png", options)? {
+        println!("{}", entry?.display());
+    }
+
+    Ok(())
+}
+```

+ 94 - 0
docs/src/files/read-write.md

@@ -0,0 +1,94 @@
+# 文件读写
+
+## 迭代文件中的内容行
+
+```rust,editable
+use std::fs::File;
+use std::io::{Write, BufReader, BufRead, Error};
+
+fn main() -> Result<(), Error> {
+    let path = "lines.txt";
+
+    // 创建文件
+    let mut output = File::create(path)?;
+    // 写入三行内容
+    write!(output, "Rust\n💖\nFun")?;
+
+    let input = File::open(path)?;
+    let buffered = BufReader::new(input);
+
+    // 迭代文件中的每一行内容,line 是字符串
+    for line in buffered.lines() {
+        println!("{}", line?);
+    }
+
+    Ok(())
+}
+```
+
+## 避免对同一个文件进行读写
+[same_file](https://docs.rs/same-file/latest/same_file/) 可以帮我们识别两个文件是否是相同的。
+
+```rust,editable
+use same_file::Handle;
+use std::fs::File;
+use std::io::{BufRead, BufReader, Error, ErrorKind};
+use std::path::Path;
+
+fn main() -> Result<(), Error> {
+    let path_to_read = Path::new("new.txt");
+
+    // 从标准输出上获取待写入的文件名
+    let stdout_handle = Handle::stdout()?;
+    // 将待写入的文件名跟待读取的文件名进行比较
+    let handle = Handle::from_path(path_to_read)?;
+
+    if stdout_handle == handle {
+        return Err(Error::new(
+            ErrorKind::Other,
+            "You are reading and writing to the same file",
+        ));
+    } else {
+        let file = File::open(&path_to_read)?;
+        let file = BufReader::new(file);
+        for (num, line) in file.lines().enumerate() {
+            println!("{} : {}", num, line?.to_uppercase());
+        }
+    }
+
+    Ok(())
+}
+```
+
+以下代码会报错,因为待写入的文件名也是 *new.txt*,跟待读取的文件名相同
+```shell
+cargo run >> ./new.txt
+```
+
+### 使用内存映射访问文件
+[memmap](https://docs.rs/memmap/) 能创建一个文件的内存映射( memory map ),然后模拟一些非顺序读。
+
+使用内存映射,意味着你将相关的索引加载到内存中,而不是通过 [seek](https://doc.rust-lang.org/std/fs/struct.File.html#method.seek) 的方式去访问文件。
+
+[Mmap::map](https://docs.rs/memmap/*/memmap/struct.Mmap.html#method.map) 函数会假定待映射的文件不会同时被其它进程修改。
+
+```rust,editable
+use memmap::Mmap;
+use std::fs::File;
+use std::io::{Write, Error};
+
+fn main() -> Result<(), Error> {
+    write!(File::create("content.txt")?, "My hovercraft is full of eels!")?;
+
+    let file = File::open("content.txt")?;
+    let map = unsafe { Mmap::map(&file)? };
+
+    let random_indexes = [0, 1, 2, 19, 22, 10, 11, 29];
+    assert_eq!(&map[3..13], b"hovercraft");
+    let random_bytes: Vec<u8> = random_indexes.iter()
+        .map(|&idx| map[idx])
+        .collect();
+    assert_eq!(&random_bytes[..], b"My loaf!");
+    Ok(())
+}
+```

+ 31 - 0
docs/src/memory/global-vars.md

@@ -0,0 +1,31 @@
+# 全局变量
+
+### 使用 lazy_static 在运行期初始化全局变量
+
+下面的例子,我们将使用 [lazy_static](https://docs.rs/lazy_static/latest/lazy_static/) 声明一个在运行期初始化( 懒求值 )的 `Hashmap`,它会被求值一次,然后保存在一个全局的 `static` 引用之后。
+
+```rust,editable
+use lazy_static::lazy_static;
+use std::collections::HashMap;
+
+lazy_static! {
+    static ref PRIVILEGES: HashMap<&'static str, Vec<&'static str>> = {
+        let mut map = HashMap::new();
+        map.insert("James", vec!["user", "admin"]);
+        map.insert("Jim", vec!["user"]);
+        map
+    };
+}
+
+fn show_access(name: &str) {
+    let access = PRIVILEGES.get(name);
+    println!("{}: {:?}", name, access);
+}
+
+fn main() {
+    let access = PRIVILEGES.get("James");
+    println!("James: {:?}", access);
+
+    show_access("Jim");
+}
+```

+ 71 - 0
docs/src/os/awesome.md

@@ -0,0 +1,71 @@
+# 操作系统
+
+操作系统范畴很大,本章节中精选的内容聚焦在用Rust实现的操作系统以及用Rust写操作系统的教程。
+
+## 目录
+| 系统 | 描述 |
+| -------- | --------- |
+| [redox](#redox) | `Unix`风格的微内核OS |
+| [tock](#tock) | 嵌入式操作系统 |
+| [theseus](#theseus) | 独特设计的OS | 
+| [writing os in rust](#writing-an-os-in-rust) | 使用Rust开发简单的操作系统 |
+| [rust-raspberrypi-OS-tutorials](#rust-raspberrypi-os-tutorials) |  Rust嵌入式系统开发教程 |
+| [rcore-os](#rcore-os) | 清华大学提供的`rcore`操作系统教程 | 
+| [edu-os](#edu-os) | 亚琛工业大学操作系统课程的配套项目 |
+
+### redox
+[redox](https://github.com/redox-os/redox) 是一个 `Unix` 风格的微内核操作系统,使用 `Rust` 实现。`redox` 的目标是安全、快速、免费、可用,它在内核设计上借鉴了很多优秀的内核,例如:`SeL4`, `MINIX`, `Plan 9`和`BSD`。
+
+但 `redox` 不仅仅是一个内核,它还是一个功能齐全的操作系统,提供了操作系统该有的功能,例如:内存分配器、文件系统、显示管理、核心工具等等。你可以大概认为它是一个 `GNU` 或 `BSD` 生态,但是是通过一门现代化、内存安全的语言实现的。
+
+> 不过据我仔细观察,redox目前的开发进度不是很活跃,不知道发生了什么,未来若有新的发现会在这里进行更新 - Sunface
+
+<img alt="redox1 screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/os/redox1.jpg?raw=true" class="center"  />
+
+<img alt="redox2 screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/os/redox2.jpeg?raw=true" class="center"  />
+
+### tock
+[tock](https://github.com/tock/tock) 是一个嵌入式操作系统,设计用于在低内存和低功耗的微控制器上运行多个并发的、相互不信任的应用程序,例如它可在 `Cortex-M` 和 `RISC-V` 平台上运行。
+
+`Tock` 使用两个核心机制保护操作系统中不同组件的安全运行:
+
+- 内核和设备驱动全部使用Rust编写,提供了很好安全性的同时,还将内核和设备进行了隔离
+- 使用了内存保护单元技术,让应用之间、应用和内核之间实现了安全隔离
+
+具体可通过这本书了解: [The Tock Book](https://book.tockos.org/introduction.html).
+
+<img alt="tock screenshot" width="100%"  src="https://book.tockos.org/imgs/imix.svg" class="center"  />
+
+### Theseus
+[Theseus](https://github.com/theseus-os/Theseus) 是从零开始构建的操作系统,完全使用Rust进行开发。它使用了新的操作系统结构、更好的状态管理,以及利用语言内设计原则将操作系统的职责(如资源管理)转移到编译器中。
+
+该OS目前尚处于早期阶段,但是看上去作者很有信心未来可以落地,如果想要了解,可以通过官方提供的[在线书籍](https://theseus-os.github.io/Theseus/book/index.html)进行学习。
+
+### Writing an OS in Rust
+[Writing an OS in Rust](https://os.phil-opp.com) 是非常有名的博客系列,专门讲解如何使用Rust来写一个简单的操作系统,配套源码在[这里](https://github.com/phil-opp/blog_os),目前已经发布了第二版。
+
+以下是`async/await`的目录截图:
+<img alt="writing-os screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/os/writing-os.jpg?raw=true" class="center"  />
+
+### rust-raspberrypi-OS-tutorials
+[rust-raspberrypi-OS-tutorials](https://github.com/rust-embedded/rust-raspberrypi-OS-tutorials) 教大家如何用Rust开发一个嵌入式操作系统,可以运行在树莓派上。这个教程讲得很细,号称手把手教学,而且是从零实现,因此很值得学习。
+
+<img alt="rrot screenshot" width="50%" height="400px" src="https://github.com/studyrs/cookbook-images/blob/main/os/rrot.jpg?raw=true" class="center"  />
+<img alt="rrot1 screenshot" width="49%" height="400px" src="https://github.com/studyrs/cookbook-images/blob/main/os/rrot1.gif?raw=true" class="center"  />
+
+### rcore-os
+[rcore-os](https://github.com/rcore-os) 是由清华大学开发的操作系统,用 Rus t实现, 与 `linux` 相兼容,主要目的目前还是用于教学,因为还有相关的配套教程,非常值得学习。目前支持的功能不完全列表如下:`linux` 兼容的 `syscall` 接口、网络协议栈、简单的文件系统、信号系统、异步IO、内核模块化。
+
+- [内核实现](https://github.com/rcore-os/rCore)
+- [配套教程](https://github.com/rcore-os/rCore-Tutorial-Book-v3) 
+
+
+以下是在树莓派上运行的图:
+<img alt="rcore screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/os/rcore.jpg?raw=true" class="center"  />
+
+
+
+### edu-os
+[edu-os](https://github.com/RWTH-OS/eduOS-rs) 是 `Unix` 风格的操作系统,用于教学目的,它是亚琛工业大学(RWTH Aachen University)操作系统课程的配套大项目,但是我并没有找到对应的课程资料,根据作者的描述,上面部分的**Writing an OS in Rust**对他有很大的启发。
+
+<img alt="eduos screenshot" width="100%"  src="https://github.com/RWTH-OS/eduOS-rs/raw/master/img/demo.gif?raw=true" class="center"  />

+ 228 - 0
docs/src/os/command.md

@@ -0,0 +1,228 @@
+# 调用系统命令
+
+### 调用一个外部命令并处理输出内容
+
+下面的代码将调用操作系统中的 `git log --oneline` 命令,然后使用 [regex](https://docs.rs/regex/*/regex/struct.Regex.html) 对它输出到 `stdout` 上的调用结果进行解析,以获取哈希值和最后 5 条提交信息( commit )。
+
+```rust,editable
+#use error_chain::error_chain;
+
+use std::process::Command;
+use regex::Regex;
+
+#error_chain!{
+#    foreign_links {
+#        Io(std::io::Error);
+#        Regex(regex::Error);
+#        Utf8(std::string::FromUtf8Error);
+#    }
+#}
+
+#[derive(PartialEq, Default, Clone, Debug)]
+struct Commit {
+    hash: String,
+    message: String,
+}
+
+fn main() -> Result<()> {
+    let output = Command::new("git").arg("log").arg("--oneline").output()?;
+
+    if !output.status.success() {
+        error_chain::bail!("Command executed with failing error code");
+    }
+
+    let pattern = Regex::new(r"(?x)
+                               ([0-9a-fA-F]+) # commit hash
+                               (.*)           # The commit message")?;
+
+    String::from_utf8(output.stdout)?
+        .lines()
+        .filter_map(|line| pattern.captures(line))
+        .map(|cap| {
+                 Commit {
+                     hash: cap[1].to_string(),
+                     message: cap[2].trim().to_string(),
+                 }
+             })
+        .take(5)
+        .for_each(|x| println!("{:?}", x));
+
+    Ok(())
+}
+```
+
+### 调用 python 解释器运行代码并检查返回的错误码
+
+```rust,editable
+#use error_chain::error_chain;
+
+use std::collections::HashSet;
+use std::io::Write;
+use std::process::{Command, Stdio};
+
+#error_chain!{
+#    errors { CmdError }
+#    foreign_links {
+#        Io(std::io::Error);
+#        Utf8(std::string::FromUtf8Error);
+#    }
+#}
+
+fn main() -> Result<()> {
+    let mut child = Command::new("python").stdin(Stdio::piped())
+        .stderr(Stdio::piped())
+        .stdout(Stdio::piped())
+        .spawn()?;
+
+    child.stdin
+        .as_mut()
+        .ok_or("Child process stdin has not been captured!")?
+        .write_all(b"import this; copyright(); credits(); exit()")?;
+
+    let output = child.wait_with_output()?;
+
+    if output.status.success() {
+        let raw_output = String::from_utf8(output.stdout)?;
+        let words = raw_output.split_whitespace()
+            .map(|s| s.to_lowercase())
+            .collect::<HashSet<_>>();
+        println!("Found {} unique words:", words.len());
+        println!("{:#?}", words);
+        Ok(())
+    } else {
+        let err = String::from_utf8(output.stderr)?;
+        error_chain::bail!("External command failed:\n {}", err)
+    }
+}
+```
+
+### 通过管道来运行外部命令
+下面的例子将显示当前目录中大小排名前十的文件和子目录,效果等效于命令 `du -ah . | sort -hr | head -n 10`。
+
+[`Command`](https://doc.rust-lang.org/std/process/struct.Command.html) 命令代表一个进程,其中父进程通过 [Stdio::piped](https://doc.rust-lang.org/std/process/struct.Stdio.html) 来捕获子进程的输出。
+
+```rust,editable
+#use error_chain::error_chain;
+
+use std::process::{Command, Stdio};
+
+#error_chain! {
+#    foreign_links {
+#        Io(std::io::Error);
+#        Utf8(std::string::FromUtf8Error);
+#    }
+#}
+
+fn main() -> Result<()> {
+    let directory = std::env::current_dir()?;
+    let mut du_output_child = Command::new("du")
+        .arg("-ah")
+        .arg(&directory)
+        .stdout(Stdio::piped())
+        .spawn()?;
+
+    if let Some(du_output) = du_output_child.stdout.take() {
+        let mut sort_output_child = Command::new("sort")
+            .arg("-hr")
+            .stdin(du_output)
+            .stdout(Stdio::piped())
+            .spawn()?;
+
+        du_output_child.wait()?;
+
+        if let Some(sort_output) = sort_output_child.stdout.take() {
+            let head_output_child = Command::new("head")
+                .args(&["-n", "10"])
+                .stdin(sort_output)
+                .stdout(Stdio::piped())
+                .spawn()?;
+
+            let head_stdout = head_output_child.wait_with_output()?;
+
+            sort_output_child.wait()?;
+
+            println!(
+                "Top 10 biggest files and directories in '{}':\n{}",
+                directory.display(),
+                String::from_utf8(head_stdout.stdout).unwrap()
+            );
+        }
+    }
+
+    Ok(())
+}
+```
+
+### 将子进程的 stdout 和 stderr 重定向到同一个文件
+下面的例子将生成一个子进程,然后将它的标准输出和标准错误输出都输出到同一个文件中。最终的效果跟 Unix 命令 `ls . oops >out.txt 2>&1` 相同。
+
+[File::try_clone](https://doc.rust-lang.org/std/fs/struct.File.html#method.try_clone) 会克隆一份文件句柄的引用,然后保证这两个句柄在写的时候会使用相同的游标位置。
+
+```rust,editable
+use std::fs::File;
+use std::io::Error;
+use std::process::{Command, Stdio};
+
+fn main() -> Result<(), Error> {
+    let outputs = File::create("out.txt")?;
+    let errors = outputs.try_clone()?;
+
+    Command::new("ls")
+        .args(&[".", "oops"])
+        .stdout(Stdio::from(outputs))
+        .stderr(Stdio::from(errors))
+        .spawn()?
+        .wait_with_output()?;
+
+    Ok(())
+}
+```
+
+### 持续处理子进程的输出
+下面的代码会创建一个管道,然后当 `BufReader` 更新时,就持续从 `stdout` 中读取数据。最终效果等同于 Unix 命令 `journalctl | grep usb`。
+
+```rust,editable
+use std::process::{Command, Stdio};
+use std::io::{BufRead, BufReader, Error, ErrorKind};
+
+fn main() -> Result<(), Error> {
+    let stdout = Command::new("journalctl")
+        .stdout(Stdio::piped())
+        .spawn()?
+        .stdout
+        .ok_or_else(|| Error::new(ErrorKind::Other,"Could not capture standard output."))?;
+
+    let reader = BufReader::new(stdout);
+
+    reader
+        .lines()
+        .filter_map(|line| line.ok())
+        .filter(|line| line.find("usb").is_some())
+        .for_each(|line| println!("{}", line));
+
+     Ok(())
+}
+```
+
+### 读取环境变量
+
+使用 [std::env::var](https://doc.rust-lang.org/std/env/fn.var.html) 可以读取系统中的环境变量。
+
+```rust,editable
+use std::env;
+use std::fs;
+use std::io::Error;
+
+fn main() -> Result<(), Error> {
+    // 读取环境变量 `CONFIG` 的值并写入到 `config_path` 中。
+    // 若 `CONFIG` 环境变量没有设置,则使用一个默认的值 "/etc/myapp/config"
+    let config_path = env::var("CONFIG")
+        .unwrap_or("/etc/myapp/config".to_string());
+
+    let config: String = fs::read_to_string(config_path)?;
+    println!("Config: {}", config);
+
+    Ok(())
+}
+```
+

+ 11 - 0
docs/src/os/processor.md

@@ -0,0 +1,11 @@
+# 处理器
+
+### 获取逻辑CPU的核心数
+
+[num_cpus](https://docs.rs/num_cpus/latest/num_cpus/) 可以用于获取逻辑和物理的 CPU 核心数,下面的例子是获取逻辑核心数。
+
+```rust,editable
+fn main() {
+    println!("Number of logical cores is {}", num_cpus::get());
+}
+```

+ 28 - 0
docs/src/protocols/tcpip.md

@@ -0,0 +1,28 @@
+# TCP/IP
+
+### 监听 TCP 端口
+
+以下代码会监听指定的 TCP 端口,并接收一条外部进入的 TCP 连接,然后将读取到的一条信息输出到标准输出( `println!` )。
+
+```rust,editable
+use std::net::{SocketAddrV4, Ipv4Addr, TcpListener};
+use std::io::{Read, Error};
+
+fn main() -> Result<(), Error> {
+    let loopback = Ipv4Addr::new(127, 0, 0, 1);
+    let socket = SocketAddrV4::new(loopback, 0);
+    let listener = TcpListener::bind(socket)?;
+    let port = listener.local_addr()?;
+    println!("Listening on {}, access this port to end the program", port);
+    let (mut tcp_stream, addr) = listener.accept()?; //block  until requested
+    println!("Connection received! {:?} is sending data.", addr);
+    let mut input = String::new();
+    let _ = tcp_stream.read_to_string(&mut input)?;
+    println!("{:?} says {}", addr, input);
+    Ok(())
+}
+```
+
+
+### 循环接收进入的 TCP 连接
+@todo

+ 202 - 0
docs/src/text/regex.md

@@ -0,0 +1,202 @@
+# 正则表达式
+
+### 验证邮件格式并取出 @ 前的信息
+下面代码使用 [regex](https://docs.rs/regex/latest/regex/) 包来验证邮件格式的正确性,然后提取出 `@` 符号前的所有内容。
+
+```rust,editable
+use lazy_static::lazy_static;
+
+use regex::Regex;
+
+fn extract_login(input: &str) -> Option<&str> {
+    lazy_static! {
+        static ref RE: Regex = Regex::new(r"(?x)
+            ^(?P<login>[^@\s]+)@
+            ([[:word:]]+\.)*
+            [[:word:]]+$
+            ").unwrap();
+    }
+    RE.captures(input).and_then(|cap| {
+        cap.name("login").map(|login| login.as_str())
+    })
+}
+
+fn main() {
+    assert_eq!(extract_login(r"I❤email@example.com"), Some(r"I❤email"));
+    assert_eq!(
+        extract_login(r"sdf+sdsfsd.as.sdsd@jhkk.d.rl"),
+        Some(r"sdf+sdsfsd.as.sdsd")
+    );
+    assert_eq!(extract_login(r"More@Than@One@at.com"), None);
+    assert_eq!(extract_login(r"Not an email@email"), None);
+}
+```
+
+### 从文本中提出 # 开头的标签
+例子对标签进行提取、排序和去重。需要注意,下面的标签仅仅是拉丁字母的,如果你要支持更多的字母,可以参考下 [Twitter 的正则语法](https://github.com/twitter/twitter-text/blob/c9fc09782efe59af4ee82855768cfaf36273e170/java/src/com/twitter/Regex.java#L255),友情提示,复杂的多!
+
+```rust,editable
+use lazy_static::lazy_static;
+
+use regex::Regex;
+use std::collections::HashSet;
+
+fn extract_hashtags(text: &str) -> HashSet<&str> {
+    lazy_static! {
+        static ref HASHTAG_REGEX : Regex = Regex::new(
+                r"\#[a-zA-Z][0-9a-zA-Z_]*"
+            ).unwrap();
+    }
+    HASHTAG_REGEX.find_iter(text).map(|mat| mat.as_str()).collect()
+}
+
+fn main() {
+    let tweet = "Hey #world, I just got my new #dog, say hello to Till. #dog #forever #2 #_ ";
+    let tags = extract_hashtags(tweet);
+    assert!(tags.contains("#dog") && tags.contains("#forever") && tags.contains("#world"));
+    assert_eq!(tags.len(), 3);
+}
+```
+
+
+### 从文本中提取出所有手机号
+[Regex::captures_iter] 可以对字符串型文本进行处理,以获取文本中的多个手机号。下面的例子适用于美国的号码。
+
+```rust,editable
+#use error_chain::error_chain;
+
+use regex::Regex;
+use std::fmt;
+
+#error_chain!{
+#    foreign_links {
+#        Regex(regex::Error);
+#        Io(std::io::Error);
+#    }
+#}
+
+struct PhoneNumber<'a> {
+    area: &'a str,
+    exchange: &'a str,
+    subscriber: &'a str,
+}
+
+impl<'a> fmt::Display for PhoneNumber<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "1 ({}) {}-{}", self.area, self.exchange, self.subscriber)
+    }
+}
+
+fn main() -> Result<()> {
+    let phone_text = "
+    +1 505 881 9292 (v) +1 505 778 2212 (c) +1 505 881 9297 (f)
+    (202) 991 9534
+    Alex 5553920011
+    1 (800) 233-2010
+    1.299.339.1020";
+
+    let re = Regex::new(
+        r#"(?x)
+          (?:\+?1)?                       # Country Code Optional
+          [\s\.]?
+          (([2-9]\d{2})|\(([2-9]\d{2})\)) # Area Code
+          [\s\.\-]?
+          ([2-9]\d{2})                    # Exchange Code
+          [\s\.\-]?
+          (\d{4})                         # Subscriber Number"#,
+    )?;
+
+    let phone_numbers = re.captures_iter(phone_text).filter_map(|cap| {
+        let groups = (cap.get(2).or(cap.get(3)), cap.get(4), cap.get(5));
+        match groups {
+            (Some(area), Some(ext), Some(sub)) => Some(PhoneNumber {
+                area: area.as_str(),
+                exchange: ext.as_str(),
+                subscriber: sub.as_str(),
+            }),
+            _ => None,
+        }
+    });
+
+    assert_eq!(
+        phone_numbers.map(|m| m.to_string()).collect::<Vec<_>>(),
+        vec![
+            "1 (505) 881-9292",
+            "1 (505) 778-2212",
+            "1 (505) 881-9297",
+            "1 (202) 991-9534",
+            "1 (555) 392-0011",
+            "1 (800) 233-2010",
+            "1 (299) 339-1020",
+        ]
+    );
+
+    Ok(())
+}
+```
+
+### 通过多个正则来过滤日志文件
+例子的目标是过滤出包含 "version X.X.X"、以 443 结尾的 IP 地址和特别的警告的日志行。
+
+值得注意的是,由于在正则中反斜杠非常常见,因此使用 `r#""` 形式的原生字符串对于开发者和使用者都更加友好。
+
+```rust,editable
+#use error_chain::error_chain;
+
+use std::fs::File;
+use std::io::{BufReader, BufRead};
+use regex::RegexSetBuilder;
+
+#error_chain! {
+#    foreign_links {
+#        Io(std::io::Error);
+#        Regex(regex::Error);
+#    }
+#}
+
+fn main() -> Result<()> {
+    let log_path = "application.log";
+    let buffered = BufReader::new(File::open(log_path)?);
+
+    let set = RegexSetBuilder::new(&[
+        r#"version "\d\.\d\.\d""#,
+        r#"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:443"#,
+        r#"warning.*timeout expired"#,
+    ]).case_insensitive(true)
+        .build()?;
+
+    buffered
+        .lines()
+        .filter_map(|line| line.ok())
+        .filter(|line| set.is_match(line.as_str()))
+        .for_each(|x| println!("{}", x));
+
+    Ok(())
+}
+```
+
+### 将文本中所有的指定模式替换成另外一种模式
+下面代码将标准的 ISO 8601 YYYY-MM-DD 日期模式替换成带有斜杠的美式英语日期。例如 `2013-01-15` -> `01/15/2013`。
+
+```rust,editable
+use lazy_static::lazy_static;
+
+use std::borrow::Cow;
+use regex::Regex;
+
+fn reformat_dates(before: &str) -> Cow<str> {
+    lazy_static! {
+        static ref ISO8601_DATE_REGEX : Regex = Regex::new(
+            r"(?P<y>\d{4})-(?P<m>\d{2})-(?P<d>\d{2})"
+            ).unwrap();
+    }
+    ISO8601_DATE_REGEX.replace_all(before, "$m/$d/$y")
+}
+
+fn main() {
+    let before = "2012-03-14, 2013-01-15 and 2014-07-05";
+    let after = reformat_dates(before);
+    assert_eq!(after, "03/14/2012, 01/15/2013 and 07/05/2014");
+}
+```
+

+ 75 - 0
docs/src/text/string.md

@@ -0,0 +1,75 @@
+# 字符串解析
+
+### 访问 Unicode 字符
+
+[unicode-segmentation]() 包的 [UnicodeSegmentation::graphemes]() 函数可以将 UTF-8 字符串收集成一个 Unicode 字符组成的数组。这样我们就可以通过索引的方式来访问对应的字符了。
+
+```rust,editable
+use unicode_segmentation::UnicodeSegmentation;
+
+fn main() {
+    let name = "José Guimarães\r\n";
+    let graphemes = UnicodeSegmentation::graphemes(name, true)
+    	.collect::<Vec<&str>>();
+    assert_eq!(graphemes[3], "é");
+}
+```
+
+### 为自定义结构体实现 FromStr 特征
+为我们的 RGB 结构体实现 `FromStr` 特征后,就可以将一个十六进制的颜色表示字符串转换成 RGB 结构体。
+
+```rust,editable
+use std::str::FromStr;
+
+#[derive(Debug, PartialEq)]
+struct RGB {
+    r: u8,
+    g: u8,
+    b: u8,
+}
+
+impl FromStr for RGB {
+    type Err = std::num::ParseIntError;
+
+
+    // 将十六进制的颜色码解析为 `RGB` 的实例
+    fn from_str(hex_code: &str) -> Result<Self, Self::Err> {
+    
+        // u8::from_str_radix(src: &str, radix: u32) 将一个字符串切片按照指定的基数转换为 u8 类型
+        let r: u8 = u8::from_str_radix(&hex_code[1..3], 16)?;
+        let g: u8 = u8::from_str_radix(&hex_code[3..5], 16)?;
+        let b: u8 = u8::from_str_radix(&hex_code[5..7], 16)?;
+
+        Ok(RGB { r, g, b })
+    }
+}
+
+fn main() {
+    let code: &str = &r"#fa7268";
+    match RGB::from_str(code) {
+        Ok(rgb) => {
+            println!(
+                r"The RGB color code is: R: {} G: {} B: {}",
+                rgb.r, rgb.g, rgb.b
+            );
+        }
+        Err(_) => {
+            println!("{} is not a valid color hex code!", code);
+        }
+    }
+
+    // 测试 from_str 是否按照预期工作
+    assert_eq!(
+        RGB::from_str(&r"#fa7268").unwrap(),
+        RGB {
+            r: 250,
+            g: 114,
+            b: 104
+        }
+    );
+}
+```
+
+
+### 实现 Display 特征
+@todo

+ 34 - 0
docs/src/web/scraping.md

@@ -0,0 +1,34 @@
+# 提取网络链接( 爬虫 )
+
+### 从目标网页 HTML 中提取出所有链接
+
+下面代码使用 [reqwest::get](https://docs.rs/reqwest/*/reqwest/fn.get.html) 发起一次 http 请求,然后通过 `select` 包的 [Document::from_read](https://docs.rs/select/*/select/document/struct.Document.html#method.from_read) 将请求的结果解析为 HTML 文档。
+
+
+```rust,editable
+#use error_chain::error_chain;
+use select::document::Document;
+use select::predicate::Name;
+
+#error_chain! {
+#      foreign_links {
+#          ReqError(reqwest::Error);
+#          IoError(std::io::Error);
+#      }
+#}
+
+#[tokio::main]
+async fn main() -> Result<()> {
+  let res = reqwest::get("https://www.rust-lang.org/en-US/")
+    .await?
+    .text()
+    .await?;
+
+  Document::from(res.as_str())
+    .find(Name("a"))
+    .filter_map(|n| n.attr("href"))
+    .for_each(|x| println!("{}", x));
+
+  Ok(())
+}
+```

+ 315 - 0
docs/theme/index.hbs

@@ -0,0 +1,315 @@
+<!DOCTYPE HTML>
+<html lang="{{ language }}" class="sidebar-visible no-js {{ default_theme }}">
+    <head>
+        <!-- Book generated using mdBook -->
+        <meta charset="UTF-8">
+        <title>{{ title }}</title>
+        {{#if is_print }}
+        <meta name="robots" content="noindex" />
+        {{/if}}
+        {{#if base_url}}
+        <base href="{{ base_url }}">
+        {{/if}}
+
+
+        <!-- Custom HTML head -->
+        {{> head}}
+
+        <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
+        <meta name="description" content="{{ description }}">
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+        <meta name="theme-color" content="#ffffff" />
+
+        {{#if favicon_svg}}
+        <link rel="icon" href="{{ path_to_root }}favicon.svg">
+        {{/if}}
+        {{#if favicon_png}}
+        <link rel="shortcut icon" href="{{ path_to_root }}favicon.png">
+        {{/if}}
+        <link rel="stylesheet" href="{{ path_to_root }}css/variables.css">
+        <link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+        <link rel="stylesheet" href="{{ path_to_root }}css/chrome.css">
+        {{#if print_enable}}
+        <link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+        {{/if}}
+
+        <!-- Fonts -->
+        <link rel="stylesheet" href="{{ path_to_root }}FontAwesome/css/font-awesome.css">
+        {{#if copy_fonts}}
+        <link rel="stylesheet" href="{{ path_to_root }}fonts/fonts.css">
+        {{/if}}
+
+        <!-- Highlight.js Stylesheets -->
+        <link rel="stylesheet" href="{{ path_to_root }}highlight.css">
+        <link rel="stylesheet" href="{{ path_to_root }}tomorrow-night.css">
+        <link rel="stylesheet" href="{{ path_to_root }}ayu-highlight.css">
+
+        <!-- Custom theme stylesheets -->
+        {{#each additional_css}}
+        <link rel="stylesheet" href="{{ ../path_to_root }}{{ this }}">
+        {{/each}}
+
+        {{#if mathjax_support}}
+        <!-- MathJax -->
+        <script async type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
+        {{/if}}
+
+    </head>
+    <body>
+        <!-- Provide site root to javascript -->
+        <script type="text/javascript">
+            var path_to_root = "{{ path_to_root }}";
+            var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "{{ preferred_dark_theme }}" : "{{ default_theme }}";
+        </script>
+
+        <!-- Work around some values being stored in localStorage wrapped in quotes -->
+        <script type="text/javascript">
+            try {
+                var theme = localStorage.getItem('mdbook-theme');
+                var sidebar = localStorage.getItem('mdbook-sidebar');
+                if (theme.startsWith('"') && theme.endsWith('"')) {
+                    localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
+                }
+                if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
+                    localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
+                }
+            } catch (e) { }
+        </script>
+
+        <!-- Set the theme before any content is loaded, prevents flash -->
+        <script type="text/javascript">
+            var theme;
+            try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
+            if (theme === null || theme === undefined) { theme = default_theme; }
+            var html = document.querySelector('html');
+            html.classList.remove('no-js')
+            html.classList.remove('{{ default_theme }}')
+            html.classList.add(theme);
+            html.classList.add('js');
+        </script>
+
+        <!-- Hide / unhide sidebar before it is displayed -->
+        <script type="text/javascript">
+            var html = document.querySelector('html');
+            var sidebar = 'hidden';
+            if (document.body.clientWidth >= 1080) {
+                try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+                sidebar = sidebar || 'visible';
+            }
+            html.classList.remove('sidebar-visible');
+            html.classList.add("sidebar-" + sidebar);
+        </script>
+
+        <nav id="sidebar" class="sidebar" aria-label="Table of contents">
+            <div class="sidebar-scrollbox">
+                {{#toc}}{{/toc}}
+            </div>
+            <div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
+        </nav>
+
+        <div id="page-wrapper" class="page-wrapper">
+
+            <div class="page">
+                {{> header}}
+                <div id="menu-bar-hover-placeholder"></div>
+                <div id="menu-bar" class="menu-bar sticky bordered">
+                    <div class="left-buttons">
+                        <button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+                            <i class="fa fa-bars"></i>
+                        </button>
+                        <button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
+                            <i class="fa fa-paint-brush"></i>
+                        </button>
+                        <ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
+                            <li role="none"><button role="menuitem" class="theme" id="light">{{ theme_option "Light" }}</button></li>
+                            <li role="none"><button role="menuitem" class="theme" id="rust">{{ theme_option "Rust" }}</button></li>
+                            <li role="none"><button role="menuitem" class="theme" id="coal">{{ theme_option "Coal" }}</button></li>
+                            <li role="none"><button role="menuitem" class="theme" id="navy">{{ theme_option "Navy" }}</button></li>
+                            <li role="none"><button role="menuitem" class="theme" id="ayu">{{ theme_option "Ayu" }}</button></li>
+                        </ul>
+                        {{#if search_enabled}}
+                        <button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+                            <i class="fa fa-search"></i>
+                        </button>
+                        {{/if}}
+                    </div>
+
+                    <h1 class="menu-title">{{ book_title }}</h1>
+
+                    <div class="right-buttons">
+                        {{#if print_enable}}
+                        <a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
+                            <i id="print-button" class="fa fa-print"></i>
+                        </a>
+                        {{/if}}
+                        {{#if git_repository_url}}
+                        <a href="{{git_repository_url}}" title="Git repository" aria-label="Git repository">
+                            <i id="git-repository-button" class="fa {{git_repository_icon}}"></i>
+                        </a>
+                        {{/if}}
+                        {{#if git_repository_edit_url}}
+                        <a href="{{git_repository_edit_url}}" title="Suggest an edit" aria-label="Suggest an edit">
+                            <i id="git-edit-button" class="fa fa-edit"></i>
+                        </a>
+                        {{/if}}
+
+                    </div>
+                </div>
+
+                {{#if search_enabled}}
+                <div id="search-wrapper" class="hidden">
+                    <form id="searchbar-outer" class="searchbar-outer">
+                        <input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+                    </form>
+                    <div id="searchresults-outer" class="searchresults-outer hidden">
+                        <div id="searchresults-header" class="searchresults-header"></div>
+                        <ul id="searchresults">
+                        </ul>
+                    </div>
+                </div>
+                {{/if}}
+
+                <!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+                <script type="text/javascript">
+                    document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+                    document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+                    Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+                        link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+                    });
+                </script>
+
+                <div id="content" class="content">
+                    <!-- Page table of contents -->
+                    <div class="sidetoc"><nav class="pagetoc"></nav></div>
+                    <main>
+                        {{{ content }}}
+                        <div id="giscus-container"></div>
+                    </main>
+
+                    <nav class="nav-wrapper" aria-label="Page navigation">
+                        <!-- Mobile navigation buttons -->
+                        {{#previous}}
+                            <a rel="prev" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+                                <i class="fa fa-angle-left"></i>
+                            </a>
+                        {{/previous}}
+
+                        {{#next}}
+                            <a rel="next" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+                                <i class="fa fa-angle-right"></i>
+                            </a>
+                        {{/next}}
+
+                        <div style="clear: both"></div>
+                    </nav>
+                </div>
+            </div>
+
+            <nav class="nav-wide-wrapper" aria-label="Page navigation">
+                {{#previous}}
+                    <a rel="prev" href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+                        <i class="fa fa-angle-left"></i>
+                    </a>
+                {{/previous}}
+
+                {{#next}}
+                    <a rel="next" href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+                        <i class="fa fa-angle-right"></i>
+                    </a>
+                {{/next}}
+            </nav>
+
+        </div>
+
+        {{#if livereload}}
+        <!-- Livereload script (if served using the cli tool) -->
+        <script type="text/javascript">
+            var socket = new WebSocket("{{{livereload}}}");
+            socket.onmessage = function (event) {
+                if (event.data === "reload") {
+                    socket.close();
+                    location.reload();
+                }
+            };
+            window.onbeforeunload = function() {
+                socket.close();
+            }
+        </script>
+        {{/if}}
+
+        {{#if google_analytics}}
+        <!-- Google Analytics Tag -->
+        <script type="text/javascript">
+            var localAddrs = ["localhost", "127.0.0.1", ""];
+            // make sure we don't activate google analytics if the developer is
+            // inspecting the book locally...
+            if (localAddrs.indexOf(document.location.hostname) === -1) {
+                (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+                (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+                m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+                })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
+                ga('create', '{{google_analytics}}', 'auto');
+                ga('send', 'pageview');
+            }
+        </script>
+        {{/if}}
+
+        {{#if playground_line_numbers}}
+        <script type="text/javascript">
+            window.playground_line_numbers = true;
+        </script>
+        {{/if}}
+
+        {{#if playground_copyable}}
+        <script type="text/javascript">
+            window.playground_copyable = true;
+        </script>
+        {{/if}}
+
+        {{#if playground_js}}
+        <script src="{{ path_to_root }}ace.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}editor.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}mode-rust.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}theme-dawn.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}theme-tomorrow_night.js" type="text/javascript" charset="utf-8"></script>
+        {{/if}}
+
+        {{#if search_js}}
+        <script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+        {{/if}}
+
+        <script src="{{ path_to_root }}clipboard.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}highlight.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+        <script type="text/javascript" charset="utf-8">
+        var pagePath = "{{ path }}"
+        </script>
+
+
+        <!-- Custom JS scripts -->
+        {{#each additional_js}}
+        <script type="text/javascript" src="{{ ../path_to_root }}{{this}}"></script>
+        {{/each}}
+
+        {{#if is_print}}
+        {{#if mathjax_support}}
+        <script type="text/javascript">
+        window.addEventListener('load', function() {
+            MathJax.Hub.Register.StartupHook('End', function() {
+                window.setTimeout(window.print, 100);
+            });
+        });
+        </script>
+        {{else}}
+        <script type="text/javascript">
+        window.addEventListener('load', function() {
+            window.setTimeout(window.print, 100);
+        });
+        </script>
+        {{/if}}
+        {{/if}}
+
+    </body>
+</html>

+ 113 - 0
docs/theme/style1.css

@@ -0,0 +1,113 @@
+@media only screen and (max-width:1080px) {
+    .sidetoc {
+        display: none !important;
+    }
+}
+
+@media only screen and (min-width:1080px) {
+    main {
+        position: relative;
+        padding-right: 170px;
+    }
+    .sidetoc {
+        margin-left: auto;
+        margin-right: auto;
+        /*left: calc(100% + (var(--content-max-width))/4 - 180px);*/
+        left: calc(100% - 200px);
+        position: absolute;
+    }
+    .pagetoc {
+        position: fixed;
+        width: 200px;
+        height: calc(100vh - var(--menu-bar-height) - 10rem);
+        overflow: auto;
+        z-index: 1000;
+    }
+    .pagetoc a {
+        border-left: 1px solid var(--sidebar-bg);
+        color: var(--fg) !important;
+        display: block;
+        padding-bottom: 5px;
+        padding-top: 5px;
+        padding-left: 10px;
+        text-align: left;
+        text-decoration: none;
+        font-size: 1.2rem;
+    }
+    .pagetoc a:hover,
+    .pagetoc a.active {
+        background: var(--sidebar-bg);
+        color: var(--sidebar-fg) !important;
+    }
+    .pagetoc .active {
+        background: var(--sidebar-bg);
+        color: var(--sidebar-fg);
+    }
+}
+
+.page-footer {
+    margin-top: 50px;
+    border-top: 1px solid #ccc;
+    overflow: hidden;
+    padding: 10px 0;
+    color: gray;
+}
+
+/* 修改章节目录的间距 */
+.chapter li.chapter-item {
+    /* 没有文件时的文字颜色 */
+    color: #939da3;
+    margin-top: 1rem;
+}
+
+/* 修改滚动条宽度 */
+::-webkit-scrollbar {
+    width: 5px;
+    height: 5px;
+}
+
+/* 表格靠左对齐 */
+table {
+    margin-left: 0 !important;
+}
+
+/* 只使用底部的页面跳转,因为左右两边的宽跳转会被 page-toc 遮盖 */
+@media only screen and (max-width: 2560px) {
+    .nav-wide-wrapper { display: none; }
+    .nav-wrapper { display: block; }
+}
+@media only screen and (max-width: 2560px) {
+    .sidebar-visible .nav-wide-wrapper { display: none; }
+    .sidebar-visible .nav-wrapper { display: block; }
+}
+
+
+/* 修改顶部图标大小 */
+/* #menu-bar {
+    font-size: 17px;
+} */
+/* 修改 github 样式 */
+.fa-github {
+    font-weight: 550;
+}
+.fa-github:after{
+    content: "星光点点比不过你的 🌟";
+    margin-left: 4px;
+}
+
+/* Fix on mobile device */
+code {
+    word-break: break-word;
+}
+
+/* 修复可编辑代码框顶部过窄的问题 */
+code.editable, .ace_scroller {
+    top: 10px;
+}
+
+/* 修改书侧边目录的区域分隔行样式 */
+.chapter .spacer {
+    background-color: #99CCFF;
+    height: 2px;
+    margin-top: 8px;
+}

+ 3 - 3
hello_world/src/main.rs

@@ -47,12 +47,12 @@ fn main() {
     }
 
     for i in _arr_a.iter() {
-        println!("值为:{}",i);
+        println!("值为:{}", i);
     }
 
-    // 无限循环 while(true) 
+    // 无限循环 while(true)
     // loop {
-        
+
     // }
 
     // 切片