Select

Операція select очікує, поки будь-який із набору ф’ючерсів буде готовий, і відповідає на результат цього ф’ючерсу. У JavaScript це схоже на Promise.race. У Python це порівнюється з asyncio.wait(task_set, return_when=asyncio.FIRST_COMPLETED).

Подібно до оператора порівняння, тіло select! має кілька гілок, кожен з яких має вигляд pattern = future => statement. Коли future готовий, його значення, що повертається, деструктурується за допомогою pattern. Потім виконується statement з отриманими змінними. Результат statement стає результатом макросу select!.

use tokio::sync::mpsc::{self, Receiver};
use tokio::time::{sleep, Duration};

#[derive(Debug, PartialEq)]
enum Animal {
    Cat { name: String },
    Dog { name: String },
}

async fn first_animal_to_finish_race(
    mut cat_rcv: Receiver<String>,
    mut dog_rcv: Receiver<String>,
) -> Option<Animal> {
    tokio::select! {
        cat_name = cat_rcv.recv() => Some(Animal::Cat { name: cat_name? }),
        dog_name = dog_rcv.recv() => Some(Animal::Dog { name: dog_name? })
    }
}

#[tokio::main]
async fn main() {
    let (cat_sender, cat_receiver) = mpsc::channel(32);
    let (dog_sender, dog_receiver) = mpsc::channel(32);
    tokio::spawn(async move {
        sleep(Duration::from_millis(500)).await;
        cat_sender.send(String::from("Фелікс")).await.expect("Не вдалося відправити кота.");
    });
    tokio::spawn(async move {
        sleep(Duration::from_millis(50)).await;
        dog_sender.send(String::from("Рекс")).await.expect("Не вдалося відправити собаку.");
    });

    let winner = first_animal_to_finish_race(cat_receiver, dog_receiver)
        .await
        .expect("Не вдалося отримати переможця");

    println!("Переможцем стає {winner:?}");
}
This slide should take about 5 minutes.
  • У цьому прикладі ми маємо перегони між котом і собакою. first_animal_to_finish_race слухає обидва канали та вибере того, який прибуде першим. Оскільки собаці потрібно 50 мс, вона виграє у кота, якому потрібно 500 мс.

  • У цьому прикладі ви можете використовувати канали oneshot, оскільки канали мають отримувати лише один send.

  • Спробуйте додати дедлайн до перегонів, продемонструвавши вибір різних видів ф’ючерсів.

  • Зауважте, що select! відкидає непорівнянні гілки, що скасовує їхні ф’ючерси. Його найлегше використовувати, коли кожне виконання команди select! створює нові ф’ючерси.

    • Альтернативою є передача &mut future замість самого ф’ючерса, але це може призвести до проблем, які далі обговорюються на слайді про закріплення.