Seleccionar

Una operación select espera hasta que un conjunto de futuros esté listo y responde al resultado de ese futuro. En JavaScript, esto es similar a Promise.race. En Python, se compara con asyncio.wait(task_set, return_when=asyncio.FIRST_COMPLETED).

Al igual que en el caso de una instrucción match, el cuerpo de select! tiene una serie de brazos, cada uno de los cuales tiene la forma pattern = future => statement. Cuando future está listo, la instrucción se ejecuta con las variables de pattern vinculadas al resultado de future.

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("Failed to send cat.");
    });
    tokio::spawn(async move {
        sleep(Duration::from_millis(50)).await;
        dog_sender
            .send(String::from("Rex"))
            .await
            .expect("Failed to send dog.");
    });

    let winner = first_animal_to_finish_race(cat_receiver, dog_receiver)
        .await
        .expect("Failed to receive winner");

    println!("Winner is {winner:?}");
}
  • En este ejemplo, tenemos una carrera entre un gato y un perro. first_animal_to_finish_race escucha a ambos canales y elige el que llegue primero. Como el perro tarda 50 ms, gana al gato, que tarda 500 ms.

  • En este ejemplo, puedes usar canales oneshot, ya que se supone que solo recibirán un send.

  • Prueba a añadir un límite a la carrera y demuestra cómo se seleccionan distintos tipos de futuros.

  • Ten en cuenta que select! elimina las ramas sin coincidencias, cancelando así sus futuros. Es más fácil de usar cuando cada ejecución de select! crea futuros.

    • También puedes enviar &mut future en lugar del futuro en sí, pero esto podría provocar problemas, como se explica más adelante en la diapositiva sobre pines.