ステーブルRustでasync traitに近いメソッド定義をする方法

# UPDATE

https://qiita.com/tronicboy/items/5852d3973dc7fd28b39d

もう`async fn`を`trait`で書けるようになったのでこの記事を見てください。

# 概要

Rustでトレートメソッドに`async`が使えないが、使えない理由と、代わりにできることを解説します。

# async traitsとは

Rustに非同期処理の文法が正規で導入される前からも、トレートの定義に非同期メソッドが書けるようにしてもらいたいという要望が長くあります。

トレートに非同期のメソッド定義は以下のような構文になります。

```rust
trait MyAsyncTrait {
  async fn answer(&self) -> usize;
}

struct Life(usize);

impl MyAsyncTrait for Life {
  async fn answer(&self) -> usize {
    self.0
  }
}
```

しかし、投稿時のステーブルRustでは、上記のコードはコンパイルされず、以下のようなエラーが出力されます。

```
functions in traits cannot be declared `async`
`async` trait functions are not currently supported
consider using the `async-trait`
```

`aysync-trait`については後々触れますが、なぜこのようなごく簡単に見えることができないのだろうか？`async {}`の非同期ブロックと`async fn foo() {}`なら問題なくRustのコンパイルが受け付けてくれるのに、なぜトレートになると、急に弱腰になるのだろうか？

実は深い訳があるのです。

# Rustにおけるトレイトの動的ディスパッチ

いったん非同期は傍に置いて、動的ディスパッチについて話しましょう。

Rustには、ジェネリック型の延長線で「このトレートを実装していれば、どんな型でもいいよ」という記法があります。

```rust
trait Greetable {
    fn greet(&self);
}

fn call_greet(o: &dyn Greetable) {
    o.greet();
}

struct Foo {}

impl Greetable for Foo {
    fn greet(&self) {
        println!("hello world, from Foo.");
    }
}

fn main() {
    let foo = Foo {};

    call_greet(&foo); // hello world, from Foo.
}
```

この記法を動的ディスパッチと言われており、どの`struct`でも`Greetable`を実装していれば引数としての条件を満たすことができます。

複数の`Greetable`を実装しているオブジェクトを引数にすることもできます。

```rust
fn greet_all(objects: &[&dyn Greetable]) {
    for obj in objects {
        obj.greet();
    }
}
```

さらに`Bar`という`struct`を定義しても同様に使えます。

```rust
struct Bar {}

impl Greetable for Bar {
    fn greet(&self) {
        println!("hello world, from Bar.");
    }
}

fn main() {
    let foo = Foo {};
    let bar = Bar {};

    call_greet(&foo); // hello world, from Foo.
    call_greet(&bar);
    greet_all(&vec![&foo, &bar]);
}
```

## 動的ディスパッチとvtableの関係

上記のロジックを見て、読者はあるいは「なぜ`call_greet`は違う`struct`でもどこにそのメソッドがあるのかを知れるんだろう」と首を傾げるのかもしれません。あるいはコンパイラーの魔法を信じることにするかもしれません。

前者のお相手をしたいのですが、動的ディスパッチはまさにそのことであり、実行時に同トレートを実装している`Foo`と`Bar`のどこをどうすれば`call_greet`が呼べるのかは、**vtable**というものを参照して`<Foo as Greetable>::greet`と<Bar as Greetable>::greet`のメモリ上におけるありかを調べられるからです。

`dyn Greetable`のような動的ジェネリックを使った時に、実はコンパイラーが裏で配線図になるvtableを生成してくれているのです。例えば上記の`Foo`と`Bar`だと、以下のような　vtableが生成されます：

```rust
struct Foo_Greetable_123123<'a> {
    greet: &'a fn(&'a Foo),
}

struct Bar_Greetable_038484<'a> {
    greet: &'a fn(&'a Bar),
}
```

上記のvtableは、実行時には動的に使っている`dyn FooBar`のポインターに裏でくっ付けられて、「ファットポインター」というものになります。つまり、`call_greet(&foo);`における`&foo`はただ単に`Foo`へのポインタだけでなく、`Foo`にも、`Foo_Greetable_123123`にも参照を持っているのです。

上記のようなファットポインタがあるから、たとえ`struct`が違えど、どこを見れば呼びたい関数(`greet`)があるのか、実行時にわかります。

## 動的ディスパッチの大きな制約

少しずつ話をまとめて非同期トレートに戻したいのですが、動的ディスパッチの仕組みを少し理解した今、その仕組みの制約について触れたいのです。

動的ディスパッチには、**ジェネリックの引数が使えない**のです。

例えば、以下のようなメソッドをGreetableに追加したらコンパイラーがご立腹に。

```rust
trait Greetable {
    fn greet(&self);

    fn greet_with_name<T>(&self, name: &T)
    where
        T: std::fmt::Display;
}

fn call_greet(o: &dyn Greetable) {
    o.greet_with_name("Austin");
}
```

コンパイラーのエラーは以下の通りです。

```
the trait `Greetable` cannot be made into an object
for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically
```

「object safe」というのがキーワードですが、引数にジェネリックを追加したことで、急に`Greetable`がオブジェクトセーフじゃなくなったのですね。

これはRustのmonomorphizationという機能が原因であってジェネリック関数をコンパイル時に実際に使われている型に具現化する仕組みのことです。

例えば、以下のように`<Foo as Greetable>::great_with_name`の`T`を`String`と`usize`に具現化したら以下のようなメソッドがコンパイラによって生成されます：

```rust
impl Greetable {
    fn greet_with_name_string(&self, name: &String) {...}

    fn greet_with_name_usize(&self, name: &usize) {...}
}
```

さらに、この拡張された`Greetable`の実装から`vtable`を作ろうとすると、複雑になってきます。

```rust
struct Foo_Greetable_038484<'a> {
    greet: &'a fn(&Foo),
    greet_with_name_string: &'a fn(&'a Foo, name: &'a String),
    greet_with_name_usize: &'a fn(&'a Foo, name: &'a usize),
}
```

実際、動的ディスパッチでどのような組み合わせが必要なのか、コンパイラーには計り知れないので、今現在のRustではこのようなvtable生成が不可能なのです。したがって、このようなジェネリックを使った関数をトレートに追加すると、オブジェクトセーフじゃないと言われます。

# 動的ディスパッチと非同期トレートの関係

上記のvtable生成の制約を知った上で、それが非同期トレートの実現と何の関係があるのか考えていきましょう。

まず、以下の`fut`の型は何になるのでしょうか？

```rust
let fut = async { 42 };
```

実は、asyncブロックは、`impl Future<Output = T>`という、動的オブジェクトを返していたのです。FutureというRustの非同期ロジックにおける`Promise`はトレートに過ぎず、非同期処理に使われているすべてのものは動的オブジェクトになっています。上記のasyncブロックは実は以下のような型がついています。

```rust
let fut: impl Future<Output = i32> = async { 42 };
```

`impl Future<Output = i32>`は動的オブジェクトで、コンパイル時にサイズがわかりません。例えば、`fut`のようなオブジェクトをトレートのメソッドの戻り値にしたら、コンパイルさせてくれません。

```rust
trait Greetable {
    fn greet(&self);

    fn greet_async(&self) -> impl Future<Output = i32>;
}
```

エラーの内容は以下の通りです。

```
`impl Trait` only allowed in function and inherent method return types, not in trait method return types
```

これは先ほどのvtableとも、Monomorphizationとも関係があります；`async fn`と`async`ブロックが返しているのは、言えばジェネリックタイプなのです。上記の`greet_async`を以下のように考えれば明確です。

```rust 
trait Greetable {
    ...

    fn greet_async<T>(&self) -> T where T: Future<Output = i32>;
}
```

これがMonomorphizationとvtableが相性悪くぶつかってしまう制約なのです。つまり、`Future<Output = i32>`を持って、オブジェクトセーフなvtableを生成することができないのです。

じゃあ、そもそも`dyn`を使わないで、ずっと`impl Future`を使えばいいのでは？と思うかもしれませんが、実はコンパイラーが生成してくれるFutureトレートの実装には深い、深い訳があって、`async fn`もしくは`async`ブロックは必ず動的オブジェクトに変換されないといけないのです。

## なぜasync fnとasyncブロックは`Pin<Box<dyn Future + Send + 'static>>`に変換される

上記の深い深い訳ですが、実はすべてのasyncが付くブロックと関数は`Pin<Box<dyn Future>>`に裏で変換されているのです。これまで出てこなかった`Pin`という新しい型も登場します...:scream:

これは、非同期処理の性質と関係があります。以下の例で考えましょう。

```rust
async fn count_from_file_and_save(path: &str, c: char) -> usize {
    let contents: String = async_read_to_string(path).await;
    // ここで段階の区切りが入る
    let count: usize = contents.chars().filter(|char| char == c).count();

    save_count_to_server(count).await;
    // ここで段階の区切り
    count
}
```

これのFutureの実装が以下のようになり得ます：

```rust
enum CountFromFileAndSave {
    Read,
    CountAndSave(String),
    Return(usize)
}

impl Future for CountFromFileAndSave {
    type Output = usize;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        loop {
            match self {
                Read => {
                    // ここで実際にファイルを読む処理を始めて、それが終わるまでここを通る。
                    // 読む処理が終わっていなければ、Wakerを渡して、「起こされる」まで進まない
                },
                CountAndSave(contents) => {
                    // コンテンツを読んで、データベースか何かにそれを保存する
                },
                Return(count) => {
                    // 処理が終わったので、数を返す
                }
            }
        }
    }
}
```

実際にもっと深掘りしてFutureの実装を模範してみると、生成される`struct`には、前の段階のステートを参照する部分があって、オブジェクトが自身の内部に対してポインターを持っているコードがあることに気づきます。

通常だと、Rustでstructの内部ポインターを作ることは不可能です。なぜなら、以下の例で考えればわかるように、その内部ポインターが不正にならないか保証できないからです。

```rust
struct InternalPointer<'a> {
    v: Vec<usize>,
    v_ref: &'a Vec<usize>,
}

let v = vec![0,1,2],
let mut int_pointer = InternalPointer {
    v_ref: &v, // 不可能だけど例のためにOKにしよう
    v,
}

int_pointer.v.push(3); // これでVecがメモリ上で再アロケートされた
int_pointer.v_ref //　これはもう、int_pointer.vに繋がっておらず、どこか不正にメモリを参照している
```

しかし、詳細に入らずに説明すれば、Futureトレートを実際に実装するためには、上記のような内部参照が必要不可欠になりますので、困ります。

ここでPinが登場するのですが、Pinは「このBoxに入っているオブジェクトは、メモリ上で動かないと約束する」という保証をしてくれるものです。

Futureの実装を`Pin<Box<**>>`に入れれば、内部参照があってもコンパイルできますし、安全に実行できます。なので、`async fn`と`async`ブロックがコンパイラーによって秘密裏で実装に展開された時に、`Pin<Box<dyn Future>>`になっているのです。

さらに、`Send`も付いてくるのは、非同期のランタイムにおいて、`Read`の時に`Future::poll`を呼んで処理をしたスレッドと、CountAndSaveの処理をするスレッドが必ずしも同じスレッドだとは限らないからです。Tokioなどは、パフォーマンス向上のために複数のスレッドで処理をしていますし、Futureの実装は必ずスレッドセーフじゃないと行けないようになっています。

最後に`'static`のライフタイムも付きますが、これはFutureが永遠にメモリに居座るという意味ではなく、いつ終わるかわからないから、Futureが完了するまでメモリに残れることを保証する意味合いの`'static`です。

完全に詳細まで踏み込んではいないのですが、結論として、動的ディスパッチができないと実行できないのです。

# ステーブルRustでどのようにしてasync traitの代わりになるものを定義できるのか

ともかく、今のRustでは思うようにトレートに非同期メソッドがかけないことが訳あってできないことを理解しましたが、代わりに何か打つ手はないのか？

あります。

まず近道から紹介すれば、Rustコンパイラーが推奨しているようにこのクレートを使えばいいです。

https://docs.rs/async-trait/latest/async_trait/

このクレートの`async_trait!`マクロを使えばできます。しかし、記事としては面白くないので、`Pin`を理解した今、実際にasync_trait`がやっていることを書いてみましょう。

## `Pin<Box<dyn Future<Output = **>>>`の戻り値を返すトレードメソッド

`async fn`が書けなえいなら、`async fn`の戻り値を返せばいいのです！

```rust
pub trait Insertable: Into<mysql_async::Params> {
    fn insert(self) -> Pin<Box<dyn Future<Output = Result<u64, mysql_async::Error>>>>;

    fn batch<T>(values: T) -> Pin<Box<dyn Future<Output = Result<(), mysql_async::Error>>>>
    where
        T: IntoIterator<Item = Self> + Send + 'static,
        T::IntoIter: Send,
        Self: Sized;
}
```

実際に上記のようなトレートを実装するとなれば、以下のようになります。

```rust
impl Insertable for WpPost {
    fn insert(
        self,
    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<u64, mysql_async::Error>>>> {
        let fut = async {
            let mut conn = get_conn().await?;

            let stmt = Self::get_stmt(&mut conn).await?;

            conn.exec_drop(stmt, self).await?;

            let post_id: u64 = conn
                .exec_first("SELECT LAST_INSERT_ID();", ())
                .await?
                .unwrap();

            Ok(post_id)
        };

        Box::pin(fut)
    }
    ...
}
```

さらに`macro_rules!`を使ってasync_traitsのクレートがやるように自動的に`async fn`を`fn foo() { Box::pin(async {...}) }`に書き換えるようにもできます。

# まとめ

長い記事になってしまいましたが、非同期のトレートメソッドがRust標準ライブラリに入らない理由と、代わりにどうやって非同期メソッドをトレート内で書けるかについて提案しました。

本記事ではRustのコンパイラーが裏で展開してくれるFutureの実装については大幅に省略したのが多少悔しいのですが、興味をお持ちの方は、ぜひrustのasync state machineについて調べていただければと思います。英語でもOKな方は、ぜひJon GjengsetさんのRustの非同期処理についての講義を聞いたらいいと思います。Pin、State Machineなど、内部的な話をより細かく深掘りしてくれます。

https://www.youtube.com/watch?v=ThjvMReOXYM&list=PLqbS7AVVErFiWDOAVrPt7aYmnuuOLYvOa&index=11&pp=iAQB

それでは非同期処理を筆者と同様に楽しく書けていることをお祈りします！