Skip to content
Async/await Introduction
step 1/7

Reading — step 1 of 7

Learn

~3 min readLifetimes, Trait Objects, and Async

Async Rust lets you write code that LOOKS synchronous but yields control while waiting on I/O. Programming Rust dedicates a chapter to this; the Async Book is the canonical reference.

Note: This is conceptual — running async code requires a runtime like Tokio, which Judge0 doesn't include. We'll show the syntax.

async fn — futures

Marking a function async makes it return a Future:

async fn fetch_data() -> String {
    // ... await some I/O ...
    String::from("data")
}

// fetch_data() doesn't run anything — it returns a Future.
let fut = fetch_data();   // Future, not String

Futures are LAZY in Rust. Calling an async fn just creates a Future. Nothing runs until the future is polled by an executor.

.await — yielding for async results

Inside an async context, .await extracts the value from a Future, suspending until the value is ready:

async fn build_response() -> String {
    let user = fetch_user().await;          // wait for fetch_user's future
    let settings = fetch_settings().await;
    format!("{} - {}", user, settings)
}

.await CAN ONLY appear inside an async fn or async block. The compiler transforms the function into a state machine that yields at each .await point.

Running futures: the executor

A future does nothing until polled. You need a runtime:

// With tokio:
#[tokio::main]
async fn main() {
    let s = fetch_data().await;
    println!("{}", s);
}

// With async-std:
fn main() {
    async_std::task::block_on(async {
        let s = fetch_data().await;
        println!("{}", s);
    });
}

The runtime polls futures, schedules them, and parks them on I/O readiness via the OS (epoll/kqueue/iocp).

Concurrency primitives

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

async fn parallel_work() {
    // Run two futures concurrently — both make progress
    let (a, b) = tokio::join!(
        async { sleep(Duration::from_millis(100)).await; 1 },
        async { sleep(Duration::from_millis(100)).await; 2 },
    );
    println!("got {} and {}", a, b);
}

tokio::spawn puts a future on the runtime; tokio::join! waits for multiple to complete; tokio::select! races them.

Async vs threads

  • Threads — each task gets its own OS thread (cheap-ish, but context switches and memory cost)
  • Async — many tasks multiplexed onto fewer threads (memory-cheap, suited for I/O-bound work)

For a server handling 10k concurrent connections, async is the answer. For CPU-bound parallelism, threads (often via rayon) are better.

Pin and self-referential structs

Futures can be self-referential — they hold pointers into their own state machine. This means they can't be moved after polling has begun. Pin is the type that prevents moving:

use std::pin::Pin;
use std::future::Future;

fn poll_once<F: Future>(fut: Pin<&mut F>) {
    // ...
}

Most user code never touches Pin directly — just .await. It matters when implementing custom futures or working with tokio::pin!.

Send and Sync for futures

A future returned from an async fn is Send if all variables it holds across .await points are Send. Rc (not Send) inside an async fn breaks this:

async fn bad() {
    let local = Rc::new(5);
    other_async_fn().await;
    println!("{}", local);   // local held across await — future is !Send
}

If you need Rc, drop it before the .await; for thread-spanning, use Arc.

Common mistakes

  • Creating a future and never awaiting it — futures do nothing on their own. The compiler usually warns.
  • Blocking inside async — calling std::thread::sleep or sync I/O blocks the entire executor thread. Use the async equivalents.
  • Holding non-Send types across .await — breaks tokio::spawn.
  • Spawning hundreds of tasks for CPU work — async is for I/O. Use rayon or threads for CPU.
  • Async trait methods — historically required boxing (async-trait crate or Box<dyn Future>). Native async fn in traits stabilized in 1.75 but with caveats.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…