Rustのバージョン1.36で先行サポートされていたasync
/.await
機能が、バージョン1.39で最終的な安定版になった。RustコアチームメンバのNiko Matsakis氏の説明によると、他言語とは異なり、Rustのasync/.await
はゼロコスト抽象化(zero-cost abstraction)である。
他の言語と同じように、Rustのasync/awaitサポートもシンタックスシュガーとして実装されている。
fnの代わりに
async fn
と記述することで利用可能なasync
機能は、コール時には単にFutrureを返す以外の何もしません。このFuture
が実行を保留していて、.await
することで起動することができるのです。
ただしRustでは、多少風変わりな構文を使用する。以下に示すのは、async
関数を定義して、それを別の関数から使用する方法である。
async fn a_function() -> u32 { }
async fn another_function() {
let r : u32 = a_function().await;
}
これで分かるように、Rustの.await
構文は、TypeScriptやC#など、キーワードとしてawait
を実装した他の多くの言語とは趣が違う。この選択によって、async関数の完了待ち合わせとシームレスなエラー伝搬に使用される?
演算子とをより自然に組み合わせた、a_function().await?
のような記述が可能になっている。
さらに重要なのは、Matsakis氏によれば、Rustのasync/.await
には実行時コストがないことだ。これはasync
関数の呼び出し時に、他の言語のような実行スケジュールを行わない、という事実による。async
関数は、そのfuture
戻り値で.await
がコールされた時のみ実行される。これは一種の"遅延"機能であり、一切のペナルティを被ることなく、futireのシーケンスを構成することが可能になる。
acync
/.await
のもうひとつのメリットは、Rustの貸借システム(borrowing system)との統合性に優れていることだ。asyncコード内での貸借は一般的に難しいため、これは非常に有用である。
Rustをプライムタイム対応にする上で必要だと多くの開発者が考えていたasync/.await
の導入について、Rustコミュニティは概ね好意的に受け入れている。
非同期コード開発は、これまで本当に大変な作業でした。featureで使用するオブジェクトはクローンする必要がありましたし、非同期呼び出しをチェーンしなければなりませんでした。条件付きリターンをサポートするために、さまざまな手管を弄する必要もありました。エラーメッセージは極めて説明口調で、原因を探るためのドキュメントやチュートリアルも限定的でした。Rust言語で非同期コードの生産性を確保するには、焼け石の上を歩くような苦行が必要だったのです。
async
/.await
のサポートは、他の人たちも言うように、非同期プログラミングを極めてシンプルなものにすることで、Rust言語のユーザビリティと開発者生産性を大きく向上する可能性があると同時に、アロケーションフリーでシングルスレッドな環境でのファームウェア/アプリケーション記述も可能にする。
しヵし一部の開発者からは、async
/.await
は不十分である、もっと高いレベルの抽象化が必要だ、という声もある。特に注目されている大きな制限のひとつは、async
/.await
コールチェーンで使用されるすべて関数は、そのパラダイムを念頭に置いて記述する必要がある、ということだ。そうしなければ、あちらこちらでプログラムがブロックされることになる。これはRust特有ではなく、一般的な問題ではあるが、解決するためにはランタイムシステムの導入が必要であるため、Rustのゼロコスト抽象化の理念が損なわれることになる。
別のデザインとして、待ち状態は従来のままで、asyncを関数修飾子ではなく式の修飾子とする方法もありました。しかし残念ながら,非同期コンパイラ変換は関数の静的プロパティであるため、この方法では分割コンパイルが不可能になります。
最後に注意すべき点として、Rustのasync
/.await
の現在の実装は"最小限の実行可能なプロダクト"という扱いであり、今後改良や拡張が行われる予定である。特に、trait
定義でのasync fn
の使用は、今後の拡張でサポートされるようになる予定である。