GGistDev

Async in Rust

Async enables concurrent I/O without blocking threads. async transforms functions into state machines returning futures.

async/await basics

async fn fetch() -> reqwest::Result<String> {
    let text = reqwest::get("https://example.com").await?.text().await?;
    Ok(text)
}

async fn returns impl Future<Output = T>. .await yields until the future is ready.

Runtimes

Use an executor like tokio or async-std to drive futures.

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let a = fetch();
    let b = fetch();
    let (ra, rb) = tokio::join!(a, b);  // concurrent
    println!("{}{}", ra?, rb?);
    Ok(())
}

Spawning tasks and cancellation

let handle = tokio::spawn(async { 42 });
let val = handle.await?;

Use tokio::select! to race tasks or add timeouts.

Avoid blocking

Do not call blocking APIs in async contexts. Use spawn_blocking for CPU‑bound work.

let result = tokio::task::spawn_blocking(|| do_cpu_work()).await?;

Traits and async

You cannot have async trait methods directly on stable without async-trait or GATs patterns. Prefer returning impl Future or use async-trait crate with care.

Streams

For sequences of async values, use futures::stream or tokio_stream and Stream trait.

Summary

  • async fn returns a future; .await drives it
  • Use a runtime (tokio) to run tasks; avoid blocking calls
  • Compose concurrency with join!/select! and prefer structured cancellation