Select

Uma operação de select espera até que qualquer uma das futures de um conjunto esteja pronta e responde ao resultado dessa future. Em JavaScript, isso é semelhante a Promise.race. Em Python, compara-se a asyncio.wait(task_set, return_when=asyncio.FIRST_COMPLETED).

Similar to a match statement, the body of select! has a number of arms, each of the form pattern = future => statement. When a future is ready, its return value is destructured by the pattern. The statement is then run with the resulting variables. The statement result becomes the result of the select! macro.

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("Felix")).await.expect("Falha ao enviar gato.");
    });
    tokio::spawn(async move {
        sleep(Duration::from_millis(50)).await;
        dog_sender.send(String::from("Rex")).await.expect("Falha ao enviar cachorro.");
    });

    let winner = first_animal_to_finish_race(cat_receiver, dog_receiver)
        .await
        .expect("Falha ao receber vencedor");

    println!("O vencedor Ă© {winner:?}");
}
  • Neste exemplo, temos uma corrida entre um gato e um cachorro. first_animal_to_finish_race escuta ambos os canais e escolherĂĄ o que chegar primeiro. Como o cachorro leva 50ms, ele vence contra o gato que leva 500ms.

  • VocĂȘ pode usar canais oneshot neste exemplo, jĂĄ que os canais devem receber apenas um send.

  • Tente adicionar um prazo para a corrida, demonstrando a seleção de diferentes tipos de futures.

  • Observe que select! descarta ramos nĂŁo correspondidos, o que cancela suas futures. É mais fĂĄcil de usar quando cada execução de select! cria novas futures.

    • Uma alternativa Ă© passar &mut future em vez da prĂłpria future, mas isso pode levar a problemas, discutidos mais adiante no slide de pinning.