Rust Impl Traitとクロージャの返却について

業務でRustを触っており, __impl Trait__ とやらに遭遇しました. 
初見では意味がわからなかったのですが, 
順序立てて理解していくと, 頭にすーっと入っていったので共有します。

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

※ 記述中. 今後修正していきます🙇

## 目次
* 抽象化を行う方法
* 具体例(引数での利用)
* 返り値での利用(引数の時と違って...)


## 抽象化を行う方法
抽象化については -> https://qiita.com/aaaoooiii/items/68386f497ee0c73222c2 

抽象化を行う方法として
* 静的ディスパッチを使った方法 => ```トレイト境界``` + ```ジェネリクス```を利用
* 動的ディスパッチを使った方法 => ```トレイトオブジェクト```を利用

が用意されている(※他にもある)
Rustでは __基本的に, 静的ディスパッチを採用__. ※ ゼロコスト抽象化などが関わってくる.

### 具体例
```std::fmt::Display```トレイトを満たしている型を引数にとり, 標準出力する関数を考える.
#### 静的ディスパッチ
静的ディスパッチを使った方法では, 以下のように```トレイト境界``` + ```ジェネリクス```を利用
```rust
fn print<T: std::fmt::Display>(arg: T) {
    println!("{}", arg);
}
```
コンパイル時に, 型ごとの関数が内部的に作成される(単相化)
```rust
// i32型用のprint
fn print_i32(arg: i32) {
    println!("{}", arg);
}

// String型用のprint
fn print_string(arg: String) {
    println!("{}", arg);
}
```
コンパイル時に内部的に関数を作成しているので速い... ここ後から書く

#### 動的ディスパッチ
動的ディスパッチを使った方法では, ```トレイトオブジェクト```を利用.

```rust
fn print(arg: Box<dyn std::fmt::Display>) {
    // メソッドの実行は略.. とりあえずこう書けるよってところだけ
    println!("{}", arg);
}
```
この例ではメソッドを実行していないが, メソッドを実行する場合に
```トレイトオブジェクト``` -> ```仮想関数テーブル``` -> ```具象型のメソッド``` -> 実行
といった手順を踏んでいるのでオーバヘッドが発生して, 少し遅くなる. 

詳しくは -> https://qiita.com/aaaoooiii/items/68386f497ee0c73222c2

### 返り値での利用
次は, 関数の返り値に抽象化を利用する方法について, 
i32型の値を引数にとり, そのまま返却する関数を考える
#### 静的ディスパッチ
まずは引数の時と同様に```静的ディスパッチ```から

```rust
fn print<T: std::fmt::Display>(num: i32) -> T {
    match num {
        0 => 1 as i32,
        _ => "hello".to_string(),
    }
}
```
これでいいかと思いきや, これでは __コンパイルエラー__ を起こす.
なぜか -> 関数の返り値はスタック上のサイズを知っている必要があるから.
言葉でもよく分からない場合のために..  __またまたまたまた具体例. どんな時に矛盾が生じるか__

静的ディスパッチなので, 内部的に型ごとの関数を作成しているはず. その時のことを考えると...
__※ ここから先は, 矛盾を出すためだけのコードなので間違っているかも..何ですけど許してください__. 
```rust
// i32型用のprint
fn print_i32(num: i32) -> i32 {
    match num {
        0 => 1 as i32,
        _ => "hello".to_string(),
    }
}

// String型用のprint
fn print_string(num: i32) -> String {
    match num {
        0 => 1 as i32,
        _ => "hello".to_string(),
    }
}
```
この時点でおかしい. 例えは```print_string```では返り値の型に```String型```を指定しているのに, ```i32型```を返却する可能性がある.

ってことで, 静的ディスパッチを使って返り値を返却するのは無理っぽい(※ 無理じゃない. 後述)


#### 動的ディスパッチ
次に動的ディスパッチを利用した場合は, 
```rust
fn print(num: i32) -> Box<dyn std::fmt::Display> {
    match num {
        0 => Box::new(1 as i32),
        _ => Box::new("hello".to_string()),
    }
}
```
こっちは, __コンパイルできる!!__

でも... 静的ディスパッチしたい.. 動的ディスパッチは遅くなるし..

ってことで __impl Trait__ 

### Impl Trait
まず何が嬉しいのか
=> 静的ディスパッチでトレイト(抽象型)を返すことができる.
ただし条件あり
__返り値の型が1つに定まる必要がある.__

```rust
fn print(num: i32) -> impl std::fmt::Display {
    match num {
        0 => Box::new(1 as i32),
        _ => Box::new(2 as i32),
    }
}
```
これならいいってこと.
ここで疑問.. 
ならこれで良くないか?
```rust
fn print(num: i32) -> i32 {
    match num {
        0 => Box::new(1 as i32),
        _ => Box::new(2 as i32),
    }
}
```
その通り. これでいい(笑)
だけどこの __impl Trai__ を使うことで嬉しいタイミングがある
それは, __クロージャを返却するとき.__ (※クロージャについての詳しい説明は他の記事で)

ただし, __クロージャの型は分からない__ ってところだけは必要なので説明

#### クロージャと型
例えば, こんなクロージャを定義したとして, この型分かるやついる?! いねえよな!
```rust
let c: ? = |arg| {
    arg + x
};
```
ってこと. 真面目に言うと __匿名構造体__ とやら
でクロージャ自体の型は分からないけど, 
__FnOnce__, __FnMut__, __Fn__ いずれかのトレイトは実装している.
↑ クロージャ3種盛りの話は違う記事で

#### 完成系
で, 何らかのクロージャを返却する関数を考える.

```rust
fn count(n: u32) -> impl FnMut() -> u32 {
    let mut num = n;
    move || {num += 1; num}
}
```
ちょっと分かりづらいけど, ```FnMut() -> u32``` <-これがトレイト
しっかり, 静的ディスパッチでトレイトを返却している
__これで完成__

#### 補足
勿論, 動的ディスパッチでクロージャを返却することもできる. 
```rust
fn count(n: u32) -> Box<dyn FnMut() -> u32> {
    let mut num = n;
    Box::new(move || {num += 1; num})
}
```
ただ, 基本的には静的ディスパッチをしたいので, この書き方はしないよね( __impl Trait__ が出てくるまではこっちが主流)って話..

### まとめ
抽象化プログラミングしたいな~
-> 引数の抽象化は簡単だけど, 返り値の抽象化だるいな~
-> 動的ディスパッチ Box<dyn T> なら割と簡単に記述できるんだけど速度がなぁ~
-> ん!? 静的ディスパッチで返却できるじゃん! しかもクロージャとの相性抜群!

って感じ？
以上！