Programmingシステムプログラマー

Rustの非同期性とFutureの動作はどのように実装されていますか?async/awaitの実装のユニークな特徴は何ですか、またそれは他の言語の類似機構とどのように異なりますか?

Hintsage AIアシスタントで面接を突破

答え

Rustの非同期性はFutureを通じて実現されており、これは将来得られる結果を表す作業のオブジェクトです。async fnを使用して宣言された関数は、Futureトレイトを実装する無名構造体(ジェネレータ)を返します。非同期コードを実行するにはランタイム(例えば、Tokioやasync-std)を使用する必要があります。標準ライブラリには組み込みのイベントループがありません。

Rustの主な特徴:

  • ゼロコスト抽象化 — コンパイラはasyncコードを状態機械に変換し、タスクが許可する場合はヒープへのアロケーションなしで実行します。
  • ネイティブガーベジコレクタの不在 — すべてのリソースは明示的に管理され、ライフタイムと所有権に特別な注意が必要です。
  • Send/Sync — スレッド間でのタスクの移動と同期の制約です。

例:

use tokio::time::sleep; use std::time::Duration; async fn foo() { println!("Hello"); sleep(Duration::from_secs(1)).await; println!("World!"); } #[tokio::main] async fn main() { foo().await; }

挑戦的な質問

Rustの非同期関数は、呼び出し時にデフォルトで即座に実行されることがありますか?なぜですか?

答え: いいえ。async fnの呼び出しは結果を返すのではなく、Futureタイプ(状態機械)のオブジェクトを返します。実際の実行には.awaitを呼び出すか、Futureをランタイムに渡す必要があります。呼び出し自体はタスクの説明を作成するだけで、実行はされません。

例:

async fn answer() -> u32 { 42 } let fut = answer(); // ここでは実行されず、futureの作成のみ let result = fut.await; // ここで実行が始まる

このテーマの微妙な点を知らないことによる実際のエラーの例


物語

ハイロードバックエンドプロジェクトで、ジュニア開発者がいくつかのasync関数を宣言しましたが、どこでも.awaitを呼び出しませんでした。そのため、主要なスレッドは同期的に実行され、パフォーマンスの低下と応答時間が3倍に増加しました。

物語

マイクロサービスでasync APIを使用し、tokioを介して相互作用しました。移行時に、開発者はasync-stdとtokioを同時に使用しようとしましたが、イベントループは1つだけである必要があることを忘れていました。その結果、両方のランタイムが競合したため、フリーズとランタイムパニックが発生しました。

物語

チームのメンバーの1人が、Future内で使用される型に対するSend/Syncの制約を忘れました。スレッド間でfutureを共有しようとした際、特定の構造体に対してSendを実装する必要があるというコンパイラエラーが発生し、状態保存のアーキテクチャを見直す必要が生じました。