---
# System prepended metadata

title: 'Programming Rust, 2nd Edtion'

---

# Programming Rust, 2nd Edtion
Start From: 2023-01-05

## 1. Systems Programmers Can Have Nice Things

## 2. A Tour of Rust

## 3. Fundamental Types

## 4. Ownership and Moves

## 5. References

## 6. Expressions

## 7. Error Handling

## 8. Crates and Modules

## 9. Structs

三種不同的 `struct`：

1. Named-Field Structs
2. Tuple-Like Structs
3. Unit-Like Structs

### Named-Field Structs

- 一般來說，Rust 中型別（包括 `struct`）的名稱，會用 `CamelCase`；而 fields 和 methods/functions，則是用 `snake_case`。
- 可以用 struct expression 來建立 struct 的數值：
  ```rust
  let image = GrayscaleMap {
      pixels: vec![0; width * height],
      size: (width, height)
  };
  ```
  - 如果 scope 中有和 fields 名稱相同的變數，可以利用 shorthand 的方式使用在 struct 數值的初始值中：
    ```rust
    fn new_map(size: (usize, usize)) -> GrayscaleMap {
        let pixels = vec![0; size.0 * size.1];
        GrayscaleMap { pixels, size }
    };
    ```
- 把 `struct` 宣告成 `pub`，並不代表 fields 也會變成 `pub`。如果要公開，就必須一個一個宣告。
- 如果想宣告兩個 values，其中大部份的 fields 都是相同的，只有少數不一樣的話，可以利用 `.. EXPR` 的語法節省打字精力。但要注意所有權的問題：沒有 `Copy` 的型別，就會被 move....
  ```rust
  let b: Boom = Boom { name: String::from("MyName"), height: 100 };
  // b.name 的所有權會被 boom1 搶走
  let boom1 = Boom { height: b.height / 2, .. b};
  // 為了不搶走 boom1.name，因此用 clone() 的。
  let boom2 = Boom { name: boom1.name.clone(), .. boom1};
  ```

### Tuple-Like Structs

```rust=
// 型別宣告
struct Bounds(usize, usize);

fn main() {
    // 使用型別宣告數值
    let image_bounds = Bounds(1024, 768);
    
    // 和 tuple 一樣，使用 .0, .1.... 去存取 fields
    let area = image_bounds.0 * image_bounds.1;
}
```

#### 該如何選擇 Named-Field or Tuple-Like Structs?

- 如果比較常會用到 `.` 去存取每一個欄位、而且每個欄位又有特別的意義，Named-Field 較好。
- 如果常會以整個 struct 的內容去做 pattern matching，Tuple-Like 較好。

另外，Tuple-Like struct 也可以用在給予單一欄位的型別新的型別意義，讓 compiler 做進一步的 type checking。這樣比單純加註解更好。

### Unit-Like Structs

只有型別資訊，沒有占記憶體的 memory 空間 --> 給 compiler 的 type system 檢查用的。

### Struct Layout

- Rust 不保證 fields 在記憶體中的排列順序；但保證一定會讓 struct 本身和 fields 放在相同的記憶體區塊中。例如 struct 是放在 stack 中，那 fields 就一定在 stack 中，不會放在 heap。當然，若是 fields 數值需要在 heap 中配置記憶體，那還是會放在 heap。
- 若是希望 Rust 用 C/C++ 的方式配置 memory layout，可以加上 `#[repr(C)]` 屬性。

### Defining Methods with `impl`

- 要在 methods 中使用 stcut value 本身，必須在 methods 的第一個參數中宣告 `self`。這和 Python 的語法類似。
- `self` 參數有不同的宣告方式：
    1. `&self`: 以 immutable ref 的方式借用。不會修改 fields 的內容。
    2. `&mut self`: 以 mutable ref 的方式借用。可能會修改 fields 的內容。
    3. `self`: 直接把 struct value 的所有權拿走。
    4. Smart pointers: 例如 `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`.
- **Type-Associated Fuction**: 不接收 `self` 的 associated function。類似 C++ 中的 static member function。呼叫的時候要用 `TypeName::function_name()` 的型式呼叫。
- 為什麼 data fields 和 methods 的宣告要分開？
    - 容易閱讀（不用在一堆 symbols 中分辨 data or methods）
    - 不是只有 structs 才有 methods。enums or even primitives... 分開宣告語法可以統一。
    - 方便實作 traits。

### Associated Constants

定義在 `impl` block 中的常數。使用時也是要用 `TypeName::CONST_NAME` 的型式。

## 10. Enums and Patterns

## 11. Traits and Generics

## 12. Operator Overloading

## 13. Utility Traits

## 14. Closures

## 15. Iterators

- Iterators: 實作了 `Iterator` 的型別。
- Iterable: 實作了 `IntoIterator` 的型別。

### Creating Iterators

- 型別可能會提供 `iter()`, `iter_mut()` 或是相關的 iterator 產生 methods（如 `chars()`, `bytes()`）。
- 型別也可以實作 `IntoIterator` trait，就有 `into_iter()` 可用。
    - `(&v).into_iter()`: 產生 item 的 ref。
    - `(&mut v).into_iter()`: 產生 item 的 mutable ref。
    - `v.into_iter()`: item 的所有權會轉移。
  不一定三種都會被實作。也就是說，三種必須分開實作。（可以參考 `std::vec::Vec` 的文件。）
  
### Iterator Adapters

- Lazy evalutaion - 在呼叫 consumer 之前，adapters 是不做事的。
- 從 closure 回傳值的角度來看 `map()`, `filter_map()` 和 `flat_map()`:
  1. `map()`: 剛剛好回傳一個 item。
  2. `filter_map()`: 回傳 1 個 or 0 個（所以是 `Option<U>`）
  3. `flat_map()`: 回傳任意個（所以是 iterator）
  傳入的都是一個值。
  
`flatten()`
:   1. 把 iterator of iterators 變成 iterator。
    2. 把 iterator of `Option<T>` 變成 iterator of T。（因為 `Option<T>` 實作了 `IntoIterator`）
    3. 把 iterator of `Result<T, E>` 變成 iterator of T。

`take()`
:   取前 `n` 個 item。

`take_while()`
:   在 pred 為 false 前的都取。

`skip()`
:   跳過 n 個

`skip_while()`
:   pred 為 true 的都跳過。

`peekable()`
:   建立 peekable（可以偷看下一個 item 的）iterator。
    Peekable iterator 會多一個叫 `peek()` 的 method。

`fuse()`
:   建立一個保證在「`next()` 第一次回傳 `None` 之後會持續回傳 `None`」的 iterator。

`rev()`
:   從後面開始反著 iterate。
    但這是 `DoubleEndedIterator` trait 提供的方法。不是所有的 iterators 都支援。
    
`inspect()`
:   主要是用來 debug 的。不會影響 item 的內容，但可以透過傳入的 closure 把值印出來看。

`chain()`
:   將兩個 Item 一樣的 iterator 串起來。
    如果兩個 iterator 都是 reversable 的話，那串起來的也是。
    
`enumerate()`
:   加上 index。
    ```rust=
    let items = ["Apple", "Banana", "Candy"];
    let ol_result: Vec<_> = items
        .into_iter()
        .enumerate()
        .map(|(idx, item)| format!("{}. {}", idx + 1, item))
        .collect();
    println!("{:?}", ol_result); // ["1. Apple", "2. Banana", "3. Candy"]
    ```

`zip()`
:   將兩個 iterator 組合起來，變成兩邊 items 的 tuples 的 iterator。
    只要有一邊回傳 `None` 了，這個組合起來的 iterator 就會回傳 `None`。

`by_ref()`
:   以 `&mut` 方式使用 iterator。

`cloned()`, `copy()`
:   用於產生 ref item 的 iterator，每一次都 clone 一份 item。

`cycle()`
:   無止盡地重覆元素。

### Consuming Iterators

`count()`
:   計算個數。

`sum()` 和 `product()`
:   總合和乘積。Item 必須為整數或浮點數，或是實作 `std::iter::Sum`/`std::iter::Product` 的型別。

`max()` 和 `min()`
:   取最大/最小值。Item 必須實作 `std::cmp::Ord`。無法處理浮點數（因為浮點數只有 `PartialOrd`）。

`max_by()` 和 `min_by()`
:   用指定的比較 closure（回傳 `std::cmp::Ordering`）取最大/最小值。

`max_by_key()` 和 `min_by_key()`
:   用 closure 回傳的值（實作 `std::cmp::Ord`）比較大小後，取最大/最小值。

`lt()`, `eq()` 和 `gt()`
:   回傳兩個 iterator 依序比較後的結果。

`any()`
:   對所有的 items 執行 closure，其中一個的結果為 true 時就回傳 true。

`all()`
:   對所有的 items 執行 closure，所有的結果為 true 時才回傳 true。

`position()`
:   從頭開始，對 items 執行 closure，回傳「第一個結果為 true 的 item」的 index 值。

`rposition()`
:   從尾巴開始，對 items 執行 closure，回傳「第一個結果為 true 的 item」的 index 值。
    - 必須實作 `DoubleEndedIterator`，才能從尾巴開始取 item。
    - 必須實作 `ExactSizeIterator`，才能算出結果的 index。（像是 `str::chars` 就無法得知詳細的 size，因為 UTF-8 的每個 `char` 長度不固定。

`fold()` 和 `rfold()`
:   將 items 依續累積運算，最後算出一個結果。
    即一般 functional programming 常見的 reduce 函數。

`try_fold()` 和 `try_rfold()`
:   - 和 `fold()`/`rfold()` 一樣，會累積運算。但依 closure 的回傳結果，可能會提前結束（不會走完所有的 items）。
      - 回傳型別為 `Result<T,E>`：若回傳 `Err(e)`，則 `try_fold()` 直接回傳 `Err(e)`；若回傳 `Ok(a)`，則會把 `a` 當成 cloure 的 accu 繼續做下去，最後回傳 `Ok(v)`。
      - 回傳型別為 `Option<T>`：若回傳 `None`，則 `try_fold()` 直接回傳 `None`；若回傳 `Some(a)`，則會把 `a` 當成 cloure 的 accu 繼續做下去，最後回傳 `Some(v)`。
      - 回傳型別為 `std::ops::ControlFlow`：若回傳 `Break(b)`，則 `try_fold()` 直接回傳 `b`；若回傳 `Continue(c)`，則會把 `c` 當成 accu 繼續做下去，最後回傳 `v`。
    - Iterator 中有很多 methods 會以 `try_fold()` 為基礎來實作，因此如果能自行實作更有效率的 `try_fold()`，可以顯著提升 iterator 的效率。

`nth()`
:   取得第 n 個 item。不會占用 iterator 的所有權，也不會改變 iterator 本身。

`nth_back()`
:   取得倒數第 n 個 item。

`last()`
:   取得最後一個 item。
    ==會耗用 iterator 中所有的 items，就算是`DoubleEndedIterator`也一樣。==
    因此，如果該 iterator 是 `DoubleEndedIterator` 的話，可以改用 `next_back()` 會比較好。
    
`find()` 和 `rfind()`
:   從 items 中找到第一個讓 closure 回傳 true 的 item，並回傳。

`find_map()`
:   傳入的 closure 要依每一個 item 算出一個 `Option<B>`；`find_map()` 會回傳第一個 `Some(b)`。

`collection()`
:   - 只要是實作了 `trait FromIterator` 的型別（例如       `std::collections::*`），都可以利用 `iterator.collect()` 來建立。
    - 建立 collection 的過程中，有時如果能知道 iterator 可能的長度，會更有效率（可以事先 allocate 需要的空間）。此時 iterator type 可以實作 `Iterator::size_hint()`，給 collection 使用。
    ```rust
    // 利用 turbofish 語法指定要呼叫哪個版本的 collect()
    let args = std::env::args().collect::<Vec<String>>();

    // 利用型別推斷幫你選擇對應的 collect()
    let args: Vec<String> = std::env::args().collect();
    ```

`extend()`
:    - 這是 `trait Extend` 提供的方法，不是 `trait Iterator`。
     - 把 iterable 的 items 附加到實作了 `Extend` 的集合上。
     
`partition()`
:   依據 closure 回傳的 `true` or `false`，將 items 分成兩個不同的 group。

`for_each()`
:   - 把每一個 item 都丟到 closure 去執行。
    - 相當於 `for`-loop，只是不能用 `break` 和 `continue`。
    - 如果 iterator 加了很多 adapters 變得很長，用 `for_each` 就比 `for`-loop 優雅。
    - 某些情況下，`for_each()` 會比 `for`-loop 快。

`try_for_each()`
:   和 `try_fold()` 很像，closure 可以回傳 `Option<_>`, `Result<_,_>` 或 `ControlFlow<_>`。一旦 `None`, `Err(_)`, `Break(_)` 的時候，就提前 return。

### 

## 16. Collections

## 17. Strings and Text

## 18. Input and Output

## 19. Concurrency

三種使用 Rust thread 功能的方法：
- Fork-join parallelism
- Channels
- Shared mutable state

### Fork-Join Parallelism

Pros:

1. Simple.
2. 沒有執行上的效率瓶頸：只有最後要 join 時 threads 才需要等其他的 threads。
3. 效率提升的記算直接單純。
   但效率提升還是不太可能達成完美的 n 倍，因為：
   - 工作無法真正平分。
   - 在合併計算結果時還是有 overheads。
4. 正確性可期，因為不會有 data racing。

Cons:

- 必須要能把工作分割成獨立的部份。不是所有的問題都可以這樣解決。

#### `spawn` 和 `join`

- `spawn()` 的參數為 `FnOnce` 的 closure。捕捉的 values 必須被 move 進 closure 中。
- 如果 `main()` 沒有呼叫 `join()` 就直接結束的話，所有還沒跑完的 threads 就會直接被殺掉，不會跑完。
- 如果 thread panic 的話，`join()` 就會回傳 `Result::Err`。不會因為 children thread panic 就害 parent thread 也 panic。

#### 在 threads 間共享 immutable data

- 我們無法直接在 closure 中使用 `&data`。因為我們無法確定 threads 的結束時間。萬一 `data` 已經被消滅了，但還有 thread 在跑，怎麼辦？
- 解決方法：使用 `Arc` = atomic reference counting (pointer)。

#### Crate `rayon`

可以很方便地把兩個 closure 丟到不同的 threads 去執行，或是把 iterator 的資料分配給不同的 threads。

- 自動依照系統 CPU core 數做最好的分配。
- 不同的 threads 中可以直覺地使用 immutable ref，不需要再額外用 `Arc` 去包裝。

### Channels

- 把資料從一個 thread 送到另一個 thread 的單行道 -- thread-safe queue。
- Channel 的兩端各被不同的 threads 擁有。Sender 只有寫入的權限，receiver 只有移出的權限。
- Value 的 ownership 透過 channel 從 sender 傳送給 receiver。
- 如果 channel 中沒有資料，receive 端就會被 block 住。

```rust=
use std::{fs, thread};
use std::sync::mpsc;    // multi-producer, single consumer

let (sender, receiver) = mpsc::channel();

let handle = thread::spawn(move || {
    for filename in documents {
        let text = fs::read_to_string(filename)?;
        if sender.send(text).is_err() {
            break;
        }
    }
    Ok(())
});
```

- `mpsc::channel()` 會產生 both sender & receiver。
- `sender` 的 ownership 會被 move 進 closure 中。
- `sender.send()` 在 `receiver` 被 dropped 時會回傳 `Err`。
- `receiver.recv()` 在 `sender` 被 dropped 時會回傳 `Err`。
- 第 6 行開始的 closure，回傳值為 `Result`，可以 handle 第 8 行中 `read_to_string()` 可以回傳的 `Err`。這個 closure 的回傳值會被放在 thread 的 `JoinHandle` 中。

#### Performance pitfalls

### `Send` & `Sync`

- Send: 此型別的資料可以在 threads 間移動。
- Sync: 此型別的資料可以用 immutable ref 的方式在 threads 間共享。

是 Sync 就一定是 Send，但反之不然。像 `Receiver<T>` 就只能 Send，但不能 Sync。

### Shared Mutability: `Mutex<T>`

- `Mutex::lock()` 回傳的是 `Result`，若為 `Err`，表示 mutex「中毒」了。
    - 其他 thread 在 lock mutex 的時候發生 panic。

## 20. Asynchronous Programming

## 21. Macros

## 22. Unsafe Code

## 23. Foreign Functions
