Select

Select ์—ฐ์‚ฐ์€ ์—ฌ๋Ÿฌ future๋“ค ๋ชจ๋‘์— ๋Œ€ํ•ด์„œ ์ค€๋น„๋  ๋•Œ ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ๋‹ค๊ฐ€, ๊ทธ ์ค‘ ์–ด๋–ค ํ•œ future๊ฐ€ ์ตœ์ดˆ๋กœ ์ค€๋น„ ์ƒํƒœ๊ฐ€ ๋˜๋ฉด ํ•ด๋‹น future์˜ ๊ฒฐ๊ณผ๊ฐ’์„ ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ์˜ Promise.race์™€ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค. ํŒŒ์ด์ฌ์—์„œ๋ผ๋ฉด 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("ํŽ ๋ฆญ์Šค")).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:?}");
}
  • In this example, we have a race between a cat and a dog. first_animal_to_finish_race listens to both channels and will pick whichever arrives first. Since the dog takes 50ms, it wins against the cat that take 500ms.

  • You can use oneshot channels in this example as the channels are supposed to receive only one send.

  • Try adding a deadline to the race, demonstrating selecting different sorts of futures.

  • Note that select! drops unmatched branches, which cancels their futures. It is easiest to use when every execution of select! creates new futures.

    • ๋Œ€์•ˆ์€ future ์ž์ฒด ๋Œ€์‹  &mut future๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(Pinning์„ ๋‹ค๋ฃฐ ๋•Œ ์ž์„ธํžˆ ์„ค๋ช…ํ•  ์˜ˆ์ •์ž„).