Asynchronous Rust

Futures

A future in Rust is a value that represents an asynchronous computation that may not have finished yet. Unlike callback-based promises in other languages (which allocate a heap object per step and push values eagerly through registered closures), Rust futures are poll-driven, zero-allocation state machines that sit inert until an executor pulls values from them via Future::poll.

Push-Based Promises (JavaScript / C#) vs. Pull-Based Futures (Rust)

Architectural Property Push-Based Promise (Promise<T> / Task<T>) Pull-Based Rust Future (impl Future<Output = T>)
Execution trigger Eager (starts running immediately when constructed) Lazy (does nothing until polled or .awaited)
Memory allocation Heap-allocates a promise object and closure per .then() step Composes nested futures inline in a single struct (0 heap allocations per .await!)
Who drives progress? Producer pushes result into registered continuation callback Executor pulls by calling pinned_future.poll(cx) when notified by Waker
Cancellation cost Requires wiring explicit AbortController / CancellationToken through every layer Zero-cost: dropping the Future immediately stops polling it and runs Drop on its active state!
Composition of Nested Futures (`OuterFuture` awaiting `InnerFuture`):

  In Heap-Based Languages (Pointer Chain of 3 Heap Allocations):
    [ Outer Promise ] ---> [ Middle Promise ] ---> [ Leaf Socket Promise ]

  In Rust (Single Contiguous State-Machine Struct — 0 Extra Heap Allocations!):
    +-------------------------- OuterFuture --------------------------+
    |  local_id: u64                                                  |
    |  +----------------------- MiddleFuture ----------------------+  |
    |  |  buffer: [u8; 32]                                         |  |
    |  |  +-------------------- LeafFuture ---------------------+  |  |
    |  |  |  socket_fd: i32                                     |  |  |
    |  |  +-----------------------------------------------------+  |  |
    |  +-----------------------------------------------------------+  |
    +-----------------------------------------------------------------+

Built-In Future Constructors and Adapters in std::future

The standard library (std::future) provides fundamental future building blocks out of the box without any external crate dependencies:

Function / Type in std::future Signature / Behavior
ready(val: T) -> Ready<T> Returns a future that resolves to Poll::Ready(val) on its very first poll
pending::<T>() -> Pending<T> Returns a future that never resolves (always returns Poll::Pending)
poll_fn(f) -> PollFn<F> Wraps a closure FnMut(&mut Context<'_>) -> Poll<T> into a Future<Output = T> without writing a custom struct!
IntoFuture Conversion trait invoked by .await

Creating Custom Futures Instantly with std::future::poll_fn

Stabilized in Rust 1.64, std::future::poll_fn constructs a Future directly from a |cx: &mut Context<'_>| -> Poll<T> closure—making it easy to write custom poll logic inline inside an async fn:

use std::future::{Future, poll_fn};
use std::pin::pin;
use std::task::{Context, Poll, Waker};

async fn countdown(start: u32) -> &'static str {
    let mut remaining = start;

    // `poll_fn` turns a closure taking `&mut Context<'_>` directly into a Future:
    poll_fn(|cx: &mut Context<'_>| {
        if remaining == 0 {
            println!("  countdown reached 0 -> Poll::Ready!");
            Poll::Ready("liftoff!")
        } else {
            println!("  countdown at {} -> waking & returning Poll::Pending", remaining);
            remaining -= 1;
            cx.waker().wake_by_ref();
            Poll::Pending
        }
    })
    .await
}

fn main() {
    let mut fut = pin!(countdown(2));
    let mut cx = Context::from_waker(Waker::noop());

    loop {
        match fut.as_mut().poll(&mut cx) {
            Poll::Pending => println!("executor: future returned Pending, polling again..."),
            Poll::Ready(msg) => {
                println!("executor: future completed with {:?}", msg);
                break;
            }
        }
    }
}
  countdown at 2 -> waking & returning Poll::Pending
executor: future returned Pending, polling again...
  countdown at 1 -> waking & returning Poll::Pending
executor: future returned Pending, polling again...
  countdown reached 0 -> Poll::Ready!
executor: future completed with "liftoff!"