LoginSignup
161
102

More than 3 years have passed since last update.

2019 年の非同期 Rust の動向調査

Last updated at Posted at 2020-01-26

この記事は 2018 年の非同期 Rust の動向調査 の続報です。

TL;DR

  • Rust 1.39.0 以降で async/await 構文が使えるようになりました
  • Future を実行するためのランタイムはいままで tokio だけでしたが、別に async-std というのができました
  • async/await を使うライブラリを選ぶときはこの2つのランタイムのどちらで動くのかを確認しましょう。 特に理由がなければ tokio を使うのがいいでしょう。

2018 年からの差分

一年前から非同期 Rust を追いかけている人向けの情報です。

組織の再編

昨年は async/await を stabilize させるための async-foundations とツールチェーンを調査する web-foundations というグループが発足しました。 しかし諸事情により Async Foundations WGAsync Ecosystem WG (rustasync) に再編されました。役割はほぼ同じです。 async/await stabilize されたのち Async Ecosystem WG は解散、 Async Foundations WGasync-book の編纂をしているようです。

rustasync から async-rs へ

rustasync チームは Web フレームワーク Tide や非同期ランタイムの romiojulix 、その上に構築した非同期ランタイムの抽象 rustasync/runtime および runtime-nativeruntime-tokioruntime-wasm を作るなど、 std 相当の安定した非同期ツールチェーンの整備に向けて活動していました。 ところが 方針が変わり runtime クレートによる抽象化をやめて tokio とはまったく別の非同期ランタイムの async-std を開発しました。 rustasync チーム は Async Ecosystem WG 亡き後も async-rshttp-rs に名前を変えて 非同期 rust 環境の整備に向けて活動しています。

tokio と async-std の違い

tokio と async-std は TcpStream 、 UdpStream、 Timer などの実装方式が違うため、互換性がありません。
現在は tokio、async-std、および tokio ベースの actix-net ランタイムに基づく3つのエコシステムが並立しています。

runtime tokio async-std actix-rt
(tokio)
scheduler work-stealing work-stealing actor & worker-pool
main tokio::main,
tokio::test
async_std::main,
async_std::test
actix_rt::main,
actix_rt::test
block_on tokio::runtime::Runtime::block_on async_std::block_on actix_rt::SystemRunner::block_on,
actix_rt::Runtime::block_on
http trait tower,
tower-http,
tower-http-service
http-service,
http-client
actix-http
http-client reqwest surf awc
http-server hyper http-service-hyper actix-http
web framework warp tide actix-web
logger tracing async-log actix-tracing
timer tokio::time futures-timer,
async_std::future::timeout
actix_rt::time
(tokio)
tls tokio-tls,
tokio-rustls
async-tls,
async-native-tls
actix-tls
tcp & udp tokio async-std actix-net
(tokio)

もともとこのような事態を避けるために runtime クレートが開発されてたのですが、 runtime は rustasync の解散とともに開発終了 しました。また rustasync チームの「std 相当の安定したAPIを持つ非同期ランタイムを提供したい」という思惑と tokio チームの開発方針が一致しなかったことが rustasync チームが tokio とは別に async-std を開発した原因のようです。

#[main] async main について

Tcp, Udp, tokio-timer などのランタイム固有の機能を使っている場合、それぞれのランタイム固有の #[main] を使う必要があります。

async-std の中で requwest (tokio::net) を使うと……

#[async_std::main]
async fn main() -> Result<(), reqwest::Error> {
    reqwest::get("http://example.com").await?; // thread 'main' panicked at 'not currently running on the Tokio runtime.'
}

tokio の中で surf (async-std) を使うと……

#[tokio::main]
async fn main() -> Result<(), surf::Exception> {
    surf::get("http://example.com/").await?.body_string().await?;
}

……パニックしません。実は surf 1.0.3 はデフォルトで ブロッキング IO の libcurlスレッドで待つことで動かしfeatures= ["hyper-client", "hyper-tls", "native-tls"] をつけると内部で runtime-tokio を呼ぶ ので動くのです。(おそらく hyper 相当の http ライブラリを tokio とは別に作るのが困難なため)

block_on について

Tcp, Udp, tokio-timer などのランタイム固有の機能を使っている場合、 futures::executor::block_on は使うことができません。
それぞれのランタイム固有の block_on を使う必要があります。

futures::executor::block_on で tcp (tokio::net) を使うと……

futures::executor::block_on(async {
    reqwest::get("http://example.com/").await.ok(); // thread 'main' panicked at 'not currently running on the Tokio runtime.'
});

パニックします。

futures::executor::block_on で timer (tokio::time) を使うと……

use std::time::Duration;

futures::executor::block_on(async {
    tokio::time::delay_for(Duration::from_millis(10)).await; // thread 'main' panicked at 'no current timer'
});

パニックします。

このように現在の rust ではどのライブラリが tokio | async-std で動くのかを見分けるのは大変困難です。

actix-web について

actix-web はもともと tokio-core を使ったアクターライブラリ actix を元に作られた Web フレームワークで、これはシングルスレッドの tokio-core を使ったワーカープールの上にアクターのランタイムを載せるものでした。ところが tokio のマルチスレッド化とともに、マルチスレッド版 tokio とは少し挙動の違う独自ランタイム(actix-rt)となってしまいました。そのため、 actix-rt の上で tokio::blocking などのマルチスレッド tokio 固有の機能を使うことはできません。さらに、 actix-web は actix-http という独自の http ライブラリを使用しており、これも hyper を利用したほかのエコシステムとは一線を画してします。

wasm-bindgen と Promise について

wasm-bindgen をつかうことで JS の Promise を Rust で await できるようになりました。これは wasm ホスト環境の JavaScript インタプリタの setTimeout や Promise などを使った非同期ランタイムとみなせます。例えば reqwest は wasm にコンパイル可能で、 http client の実装は fetch API を使っています。詳細は wasm-pack で JS の Promise を await できる非同期 Rust を書いて node.js で動かす を参照してください。

その他のランタイム

rust の async ランタイムは誰でも作ることができます。 tokio や async-std の他にも組み込み向けのランタイム embrio や Google が開発中の fuchsia OS で使われている ランタイム もあるようです。fuchsia については Async Interview #2: cramertj が詳しいです。

何を使えばいいの?

async-std はエコシステムは登場してまだ半年も経っておらず 2020 年現在まだまだ開発中です。 tokio の上で reqwest と warp (または rweb) を使うのがいいと思います。 AWS では rusoto および aws-lambda-rust-runtime を使うことになります。 どちらも futures-0.3 や tokio 0.2 に未対応(2020-01-26現在) です。 特に rusoto は tokio-compatfutures-compat などを使うことになるでしょう。mysql や postgres を diesel で使いたい人は、diesel はまだ非同期 IO に対応していない ので r2d2 でコネクションプーリングをした上で actix-rt の worker を使うか tokio::blocking を使うなどの工夫が必要です。redis クライアントは redis-rs でいいでしょう。

ただ現時点では actix-web のほうがサンプルコードは充実しています。参考になりそうなリポジトリへのリンクはこちらです。

とはいえ Rust の非同期環境はようやく core::future と async/await が入った段階で、今後どうなっていくのかはまだまだ不透明です。

2019-01 から 2020-05 までの時系列

太字記事は Rust の async/await についての特にオススメの記事、および影響の大きな記事です。

2020-07

2020-05

2020-04

2020-03

2020-02

2020-01

2019-12

2019-11

2019-10

2019-09

2019-08

2019-07

2019-06

2019-05

2019-04

2019-03

2019-02

2019-01

161
102
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
161
102