tokio::select!

tokio::select! is a macro that waits on several async operations at once and runs the branch of the first one that completes.

Usecases of tokio::select!

1. Network read wait
2. process work from a channel or stop when Ctrl+C / shutdown channel fires.

tokio::select! vs POSIX select() system call

Names sound similar but they live at different layers.

select() (POSIX syscall) tokio::select! (Rust macro)
Layer OS kernel — watches file descriptors (sockets, pipes) User space — watches async tasks / futures on Tokio runtime
Blocking? Blocks the OS thread until a fd is ready (or timeout) Non-blocking — task yields; thread can run other tasks
What you pass in fd sets: readfds, writefds, … Rust futures: rx.recv(), sleep(), …
Typical use C servers: one thread, many client sockets Async Rust: timeouts, shutdown, multiplexing channels
Relation Tokio’s reactor uses epoll / IOCP under the hood (similar idea to select/poll, but faster) Built on top of that reactor — you rarely call select(2) yourself in Tokio code

Simple example — message or timeout

Wait for a value on a channel. If nothing arrives in 2 seconds, print “timeout” and exit.

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

#[tokio::main]
async fn main() {
    let (tx, mut rx) = mpsc::channel::<String>(1);

    // Another task sends a message after 3 seconds (too late)
    tokio::spawn(async move {
        sleep(Duration::from_secs(3)).await;
        let _ = tx.send(String::from("hello")).await;
    });

    tokio::select! {
        maybe_msg = rx.recv() => {
            match maybe_msg {
                Some(msg) => println!("got: {msg}"),
                None => println!("channel closed"),
            }
        }
        _ = sleep(Duration::from_secs(2)) => {
            println!("timeout — no message in 2 sec");
        }
    }
}

// Output: timeout — no message in 2 sec
// (recv branch was cancelled when sleep finished first)