Asynchronous Rust

Pin and Futures

Because Future::poll takes self: Pin<&mut Self> instead of &mut self, every future must be pinned in memory before it can be polled directly. Pinning guarantees that once an async block starts executing and creates internal references across an .await point, the future's state machine will never be moved to a different memory address.

When You Need Explicit Pinning vs. When .await Handles It Automatically

In 95% of async code, expr.await pins the future automatically inside the enclosing state machine! You only need to pin a future explicitly (using std::pin::pin!(fut) on the stack or Box::pin(fut) on the heap) in four situations:

Situation Requiring Explicit Pinning Why Explicit Pinning Is Required Recommended Solution
1. Calling .poll(&mut cx) directly Receiver is self: Pin<&mut Self> let mut fut = std::pin::pin!(my_async_fn());
2. Passing a &mut F future by reference into a loop / select! Polling &mut F uses impl<F: Future + Unpin> Future for &mut F, which requires F: Unpin! (Pin<&mut F> implements Future + Unpin for any F!) let mut fut = std::pin::pin!(my_async_fn()); then pass &mut fut
3. Storing a future in a struct or dyn Future collection async blocks have anonymous !Unpin types Pin<Box<dyn Future<Output = T> + Send>> via Box::pin(fut)
4. Recursive async fn A recursive async fn embeds its own future type inside its state machine (infinite size E0733 unless boxed) Wrap the recursive call in Box::pin(recurse(...)).await
Why `&mut my_async_fn()` Does Not Implement `Future`, While `pin!(my_async_fn())` Does:

  let mut raw_fut = my_async_fn();    // Type: `impl Future` (which is `!Unpin`!)
  (&mut raw_fut).await;               // ❌ Error E0277: `&mut F` only implements `Future` if `F: Unpin`!

  let mut pinned_fut = pin!(my_async_fn()); // Type: `Pin<&mut impl Future>` (ALWAYS `Unpin`!)
  (&mut pinned_fut).await;                  // ✅ Compiles with 0 heap allocations!

Reusing a Future by Reference Across Multiple Polls (std::pin::pin!)

Because Pin<&mut F> implements Future for every F: Future (even when F: !Unpin), pinning a future on the stack with std::pin::pin! allows passing &mut pinned_fut into helper functions without consuming the future or allocating on the heap:

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

// Requires `F: Future + Unpin` so it can call `Pin::new(fut).poll(cx)` safely:
fn poll_once<F: Future + Unpin>(fut: &mut F) -> Poll<F::Output> {
    let mut cx = Context::from_waker(Waker::noop());
    std::pin::Pin::new(fut).poll(&mut cx)
}

async fn compute_value() -> i32 {
    let local = [10, 20, 12];
    let slice = &local[..];
    std::future::ready(()).await;
    slice.iter().sum()
}

fn main() {
    // `pin!(...)` pins the `!Unpin` async future on the stack as `Pin<&mut _>`,
    // and `Pin<&mut _>` implements both `Future` AND `Unpin`!
    let mut pinned = pin!(compute_value());

    let result = poll_once(&mut pinned);
    println!("polled via &mut Pin<&mut F>: {:?}", result);
}
polled via &mut Pin<&mut F>: Ready(42)

Recursive async fn with Box::pin

Just like a recursive struct or enum, an async fn that .awaits a recursive call to itself would have an infinitely sized state machine (E0733: recursion in an async fn requires boxing). Wrapping the recursive call in Box::pin(...) bounds the state-machine size to a single pointer:

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

fn async_factorial(n: u64) -> Pin<Box<dyn Future<Output = u64>>> {
    Box::pin(async move {
        if n <= 1 {
            1
        } else {
            n * async_factorial(n - 1).await
        }
    })
}

fn main() {
    let mut fut = pin!(async_factorial(5));
    let mut cx = Context::from_waker(Waker::noop());
    println!("5! = {:?}", fut.as_mut().poll(&mut cx));
}
5! = Ready(120)