Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

join!

futures::join マクロを使うと、複数の異なる Future をすべて並行に実行しながら、それらが完了するのを待てます。

join!

複数の非同期操作を実行するとき、単純にそれらを順番に .await したくなるかもしれません。

async fn get_book_and_music() -> (Book, Music) {
    let book = get_book().await;
    let music = get_music().await;
    (book, music)
}

しかし、これは必要以上に遅くなります。なぜなら、get_book が完了するまで get_music の実行を試み始めないためです。他の言語では、Future が暗黙的に完了まで実行されることがあるため、まず各 async fn を呼び出して Future を開始し、その後で両方を await することで、2 つの操作を並行に実行できます。

// WRONG -- don't do this
async fn get_book_and_music() -> (Book, Music) {
    let book_future = get_book();
    let music_future = get_music();
    (book_future.await, music_future.await)
}

しかし、Rust の Future は能動的に .await されるまで何も処理しません。つまり、上記の 2 つのコードスニペットはどちらも、book_futuremusic_future を並行に実行するのではなく、順番に実行します。2 つの Future を正しく並行に実行するには、futures::join! を使用します。

use futures::join;

async fn get_book_and_music() -> (Book, Music) {
    let book_fut = get_book();
    let music_fut = get_music();
    join!(book_fut, music_fut)
}

join! が返す値は、渡された各 Future の出力を含むタプルです。

try_join!

Result を返す Future では、join! ではなく try_join! の使用を検討してください。join! はすべてのサブ Future が完了して初めて完了するため、サブ Future の 1 つが Err を返した後でも、他の Future の処理を継続します。

join! とは異なり、try_join! はサブ Future の 1 つがエラーを返すと即座に完了します。

use futures::try_join;

async fn get_book() -> Result<Book, String> { /* ... */ Ok(Book) }
async fn get_music() -> Result<Music, String> { /* ... */ Ok(Music) }

async fn get_book_and_music() -> Result<(Book, Music), String> {
    let book_fut = get_book();
    let music_fut = get_music();
    try_join!(book_fut, music_fut)
}

try_join! に渡される Future は、すべて同じエラー型でなければならないことに注意してください。エラー型を統合するには、futures::future::TryFutureExt.map_err(|e| ...) 関数と .err_into() 関数の使用を検討してください。

use futures::{
    future::TryFutureExt,
    try_join,
};

async fn get_book() -> Result<Book, ()> { /* ... */ Ok(Book) }
async fn get_music() -> Result<Music, String> { /* ... */ Ok(Music) }

async fn get_book_and_music() -> Result<(Book, Music), String> {
    let book_fut = get_book().map_err(|()| "Unable to get book".to_string());
    let music_fut = get_music();
    try_join!(book_fut, music_fut)
}