---
# System prepended metadata

title: Rust 讀書會 Week 4

---

# Rust 讀書會 Week 4

[toc]

## CH5

## CH10

### 10-1 泛型資料型別

用法：用 <> 標示，這邊聲明一個泛型 `T`，可以讓 compiler 自己去檢查。

**枚舉**
```rust=
enum Option<T> {
    Some(T),
    None,
}
```

**結構體**
注意，impl method 是附加的形式
1. 所有的 type 都會有 `x()`, `y()`
2. `warn()` 只有 `&str` 才有
> Point\<i32\> 有 x, y 兩個 method
> Point\<&str\> 有 x, y, warn 三個 method

```rust=
struct Point<T> {
    x: T,
    y: T,
}

// 泛型資料的 method
impl<T> Point<T> {
    fn x(&self) -> &T {
        &self.x
    }
    fn y(&self) -> &T {
        &self.y
    }
}

// 專門為特定的型別實做 method
impl Point<&str> {
    fn warn(&self) {
        println!("Don't use &str, String");
    }
}

fn main() {
    let p = Point { x: "10", y: "20" };
    let q = Point { x: 10, y: 20 };
    p.warn();
    println!("x: {}, y: {}", p.x(), p.y());
    // q.warn(); // error
    println!("x: {}, y: {}", p.x(), p.y());
}
```
---
### 10-2 trait

Trait 用於定義共同行為，我們可以使用特徵定義來抽象出共同行為。類似於其他語言的 interface。

用法：`trait` 關鍵字

以 Point 為範例，現在我們要推廣二維到三維，然後要計算點到 origin 的距離。二維三維都能計算到原點的距離，所以我們把這個特徵抽出來，取名叫 `Utils`。然後先為 `Point2D` 實做它，如何實做寫在範例裡面。

**Point 範例 - Version 1**
```rust=
trait Utils {
  fn distance_to_origin(&self) -> f32;
}

struct Point2D {
  x: f32,
  y: f32,
}

struct Point3D {
  x: f32,
  y: f32,
  z: f32,
}

impl Utils for Point2D {
  fn distance_to_origin(&self) -> f32 {
    (self.x.powi(2) + self.y.powi(2)).sqrt()
  }
}

fn main() {
    let p2d = Point2D { x:3f32, y:4f32 };
    println!("{}", p2d.distance_to_origin());
}
```

阿你說 3D 呢，懶得實做它 compiler 會過不去。所以我們先 implement 一個暫時的。在 `Utils` 補充完這個 function，這個方式稱作為**預設實做**，如果型別沒有實做的話，就會用預設實做。

**Point 範例 - Version 2**
```rust=
trait Utils {
  fn distance_to_origin(&self) -> f32 {
      println!("not implement yet");
      0f32
  }
}

struct Point2D {
  x: f32,
  y: f32,
}

struct Point3D {
  x: f32,
  y: f32,
  z: f32,
}

impl Utils for Point2D {
  fn distance_to_origin(&self) -> f32 {
    (self.x.powi(2) + self.y.powi(2)).sqrt()
  }
}

impl Utils for Point3D {}

fn main() {
    let p2d = Point2D { x:3f32, y:4f32 };
    println!("{}", p2d.distance_to_origin());
    let p3d = Point3D { x:3f32, y:4f32, z:5f32 };
    println!("{}", p3d.distance_to_origin());
}
```
---
在其他語言中，我們也可以看到 interface 在作為參數傳遞。當然 trait 也可以。範例中，兩種寫法是一樣的，一個透過 argument，一個透過聲明 `T`。但是你會發現，這樣 compiler 不會過，因為 compiler 沒辦法確認每個傳入的 type 是不是都有實做 trait，所以預設不會有任何 trait 在裡面。

**trait 作為參數傳入 - Version1**
```rust=
// way 1
fn print_point(t: &impl Utils) {
    println!("{:?}", t);
}

// way 2
fn print_point<T: Utils>(t: &T) {
    println!("{:?}", t);
}
```

好吧，我們來看一下 Rust 如何印出資料。Rust 印出資料都是透過 trait，像是 `println!("{}")` 會使用的 `Display` 這個 trait。我們常用的 `println!("{:?}")`、`println!("{:#?}")`，則是使用的 `Debug`。

V1 的版本沒有 `Debug`，我們來修改一下。

**trait 作為參數傳入 - Version2**
```rust=
// 引入型別
use std::fmt::Debug;

// 利用 derive 讓型別利用 Debug
#[derive(Debug)]
struct Point2D {
  x: f32,
  y: f32,
}

#[derive(Debug)]
struct Point3D {
  x: f32,
  y: f32,
  z: f32,
}

// 現在型別有兩個 trait 了，補上 trait，利用 +
fn print_point<T: Utils + Debug>(t: &T) {
    println!("{:?}", t);
}
```

當你的 trait 用來越多，你也可以使用 `where`
```rust=
fn print_point<T>(t: &T)
    where T: Utils + Debug,
{
    println!("{:?}", t);
}
```

interface 能作為參數傳入，當然也可以作為回傳值。
```rust=
fn new_point_2d(x: f32, y: f32) -> (impl Utils + Debug) {
    Point2D {
        x,
        y,
    }
}
```
---
透過特徵界限來選擇性實作方法，我們在泛型型別可以針對特定型別實做，Trait 也可以。

```rust=
use std::fmt::Display;

struct Pair<T> {
    x: T,
    y: T,
}

impl<T> Pair<T> {
    fn new(x: T, y: T) -> Self {
        Self { x, y }
    }
}

impl<T: Display + PartialOrd> Pair<T> {
    fn cmp_display(&self) {
        if self.x >= self.y {
            println!("The largest member is x = {}", self.x);
        } else {
            println!("The largest member is y = {}", self.y);
        }
    }
}
```
[rust playground](https://play.rust-lang.org/?code=fn%20main()%20%7B%0A%20%20%20%20let%20more_numbers%20%3D%20%5B10%2C%2012%2C%2018%5D%3B%0A%20%20%20%20let%20mut%20all_numbers%20%3D%20Vec%3A%3Anew()%3B%0A%20%20%20%20all_numbers.push(1)%3B%0A%20%20%20%20all_numbers.push(2)%3B%0A%20%20%20%20all_numbers.extend(%26more_numbers)%3B%0A%20%20%20%20%0A%20%20%20%20println!(%22%7B%3A%3F%7D%22%2C%20all_numbers)%3B%0A%7D&version=stable)

---
### 10-3 (暫補)
