Rustで抽象化プログラミング 静的ディスパッチと動的ディスパッチもちょっと..

Rustでの抽象化プログラミングについてのメモ
ついでに静的ディスパッチと動的ディスパッチについても軽くまとめてみる...

__自己満(腑に落ちる) > 正しい__ の記事なので, 言葉の使い方が間違っているところも大目に見てください. + 後半になるにつれて雑になります..

## 目次
* 抽象化プログラミングについて
* 静的ディスパッチと動的ディスパッチについて


## 抽象化プログラミング
なぜ必要なのか, __抽象化を行わない具体例__ と __抽象化を行う具体例__ を使って説明。

### ユースケース
何らかのI/O処理が可能なデータから, データを読み込んで, 指定した文字列が含まれているかを判定する関数を作成する.

以下の3つのI/O処理が可能なデータ __※ 型名は擬似名称なもの__ を対象
1. ファイルから読み取ったデータ: __File型__
2. ネットワークから読み取ったデータ: __NetWork型__
3. キーボードから読み取ったデータ: __Keyboard型__

いずれもI/O処理が可能なトレイト __I/O__ を実装しているとする. 
※ __I/O__ トレイトは ```write()``` や ```read()``` を定義

### 抽象化を行わない場合
それぞれの型ごとに, 関数を作成する必要がある.<font color="Red"> __※ 以下は全て擬似的なコードです.__ </font>
##### 1. ファイルからデータを読み込んで, 指定した文字列が含まれているか判定する関数
```rust
fn grep_file(file: File, string: String) -> bool {
    let buffer = file.read();
    is_exit(buffer, String) //読み込んだデータに指定した文字列が含まれているか判定 真偽値を返す
}
```
##### 2. ネットワークからデータを読み込んで(受け取って), 指定した文字列が含まれているか判定する関数
```rust
fn grep_network(nw: NetWork, string: String) -> bool {
    let buffer = nw.read();
    is_exit(buffer, String)
}
```
##### 3. キーボードからデータを読み込んで, 指定した文字列が含まれているか判定する関数
```rust
fn grep_keyboard(keyboard: KeyBoard, string: String) -> bool {
    let buffer = keyboard.read();
    is_exit(buffer, String) 
}
```

### 抽象化を行う場合
3つの型は, 全て __I/O__ トレイトを満たしているので, __トレイト境界__ と __ジェネリクス__
を使って, 以下のように記述することができる.
```rust
fn grep<T: IO>(io: T) -> bool {
    let buffer = io.read();
    is_exit(buffer, String) 
}
```
__それぞれの型についての関数を書かずに済む__ ので 楽だし見やすいよね~_
... 以上!!

と終わらせたいところでけど, あとひと話題 ↓

## 静的ディスパッチと動的ディスパッチについて
抽象化を行った場合で, __トレイト境界__ と __ジェネリクス__ を使ったけど, 他にも抽象化を行う方法がある. そのひとつが __トレイトオブジェクト__ を利用した方法

さっきのコードをトレイトオブジェクトを利用して記述すると以下のようになる.
※```Box```の説明は略
```rust
fn grep(io: Box<dyn I/O>) -> bool {
    let buffer = io.read();
    is_exit(buffer, String) 
}
```

こんな感じに書くこともできる. ここで疑問 => __何が違うん?__ と
ここで __静的ディスパッチ__ と __動的ディスパッチ__ が出てくる.

### 結論
結論でもないけど.. うまくまとめられない
### 静的ディスパッチ
__内部的__ にそれぞれの型ごとの関数を作成している. 
```rust
fn grep_file(file: File, string: String) -> bool {
    let buffer = file.read();
    is_exit(buffer, String) //読み込んだデータに指定した文字列が含まれているか判定 真偽値を返す
}
```
```rust
fn grep_network(nw: NetWork, string: String) -> bool {
    let buffer = nw.read();
    is_exit(buffer, String)
}
```
```rust
fn grep_keyboard(keyboard: KeyBoard, string: String) -> bool {
    let buffer = keyboard.read();
    is_exit(buffer, String) 
}
```
さっきの抽象化しない時と変わんないじゃん... と思いきや内部的なので.. 書く分には楽だよね
##### メリット
中のメソッド```2行目: .read()```の実行が速い
##### デメリット
内部的には型ごとに関数を作成しているので, 成果物のサイズは大きくなる

### 動的ディスパッチ
トレイトオブジェクトと仮想関数テーブルを介してメソッドの実行 ```2行目: io.read()```を実行する.... 何言うてんの?.. ということでまずは用語の説明

##### トレイトオブジェクト
簡単に説明.. 正しいかは置いておいて(冒頭見てください)
イメージとしてはこんな感じ
```rust
struct TraitObject {
    data: *mut (), //具象型へのポインタ
    vtable: *mut (), // 仮想関数テーブル
}
```

##### 仮想関数テーブル (vtable)
簡単いうと, __関数ポインタのテーブル__ 
この記事でのユースケースだと, こんなイメージ?
```rust
File型のwrite()    : 0x7ff29bc02ce0 //実際のバイナリへのアドレス
File型のread()     : 0x7ff29bc03ce0
NwtWork型のwrite() : 0x7ff29bc04ce0
NwtWork型のread()  : 0x7ff29bc05ce0
KeyBoard型のwrite(): 0x7ff29bc06ce0
KeyBoard型のread() : 0x7ff29bc07ce0
```
#### ユースケース
例えば, Network型のオブジェクトがgrep関数に渡されて実行されると
```rust
fn grep(io: Box<dyn I/O>) -> bool {
    let buffer = io.read();
    is_exit(buffer, String) 
}

fn main() {
    let nw = NetWork::new();
    let result = grep(Box::new(nw));
}
```
1. grep()にNetWork型のオブジェクトが, トレイトオブジェクトとして渡される
1. 2行目のread()は, 内部的には仮想関数テーブルを参照した後に実行

みたいなイメージ
なので.
##### メリット
成果物のサイズは小さい 
##### デメリット
関数内のメソッドの実行が多少遅くなる(仮想関数テーブルを参照しているため)


### まとめ
抽象化プログラミングをすることで, 楽だし見やすくなる
抽象化の方法には2つの方法(実際にはまだある. ex. Enumなど)があって, 
* 静的ディスパッチは成果物のサイズは大きくなっちゃうけど, 速度は速い.
* 動的ディスパッチは成果物のサイズは小さいけど, 速度が落ちてしまう.

こんな感じでどうでしょう？