Skip to content

Q068. How does tokio::select! work, and what happens to losing branches?

Short Answer

tokio::select! polls several branch futures on the current task and runs the handler for the first matching branch that becomes ready. The other branch futures are dropped when the select! expression ends unless they are borrowed futures stored outside it. This is concurrent but not necessarily parallel. Losing operations must be cancellation-safe, especially when select! is repeated in a loop.

Deep Dive

select! first evaluates branch preconditions, creates enabled futures, and polls them. By default Tokio randomizes which branch is checked first to reduce starvation when several are ready. biased; uses source order and makes fairness the programmer's responsibility.

A branch pattern can reject a ready output, disabling that branch for the remainder of the current macro call. An else branch runs only when all branches are disabled; otherwise no matching branch is a panic.

The key design question is what dropping a losing future means. Reading one framed message may be cancellation-safe, while read_exact, queued lock acquisition, or a multi-step protocol may lose partial progress or queue position. Sometimes the future must be created outside the loop and pinned so it can resume rather than restart.

Internal Model

All branch futures live inside one combined future generated by the macro. One poll of the parent polls enabled branches until one completes or all are pending.

After a handler is selected, local losing futures are dropped, running destructors for their initialized state. No automatic rollback of external side effects occurs.

Example

rust
use std::time::Duration;

#[tokio::main]
async fn main() {
    let outcome = tokio::select! {
        _ = tokio::time::sleep(Duration::from_millis(1)) => "timer",
        _ = std::future::pending::<()>() => "never",
    };

    assert_eq!(outcome, "timer");
}

Common Mistakes

  • Saying select! waits for all branches.
  • Assuming the losing operation is rolled back automatically.
  • Using biased; without ensuring low-priority branches cannot starve.

Follow-ups

  • Which Tokio channel receive operations are cancellation-safe?
  • How can a future be retained across iterations of a select! loop?
  • Why does randomized polling order have a CPU cost?

References

  • Tokio Documentation