An async block creates a future from a block of code. Values captured from the surrounding scope can be awaited inside it.

Program

Play the program to choose a bonus value and compute a total inside an async block.

bonus
async_block.rs
Replay: real traced execution (multi-file project)
use std::future::Future;
use std::pin::pin;
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};

fn main() {
    let bonus = 5;
    let future = async {
        let left = ready_value(10).await;
        let right = ready_value(bonus).await;
        left + right
    };
    let total = block_on(future);
    println!("{total}");
}

async fn ready_value(value: i32) -> i32 {
    value
}

fn block_on<F: Future>(future: F) -> F::Output {
    let waker = noop_waker();
    let mut context = Context::from_waker(&waker);
    let mut future = pin!(future);
    loop {
        match future.as_mut().poll(&mut context) {
            Poll::Ready(value) => return value,
            Poll::Pending => {}
        }
    }
}

fn noop_waker() -> Waker {
    unsafe { Waker::from_raw(noop_raw_waker()) }
}

fn noop_raw_waker() -> RawWaker {
    RawWaker::new(std::ptr::null(), &VTABLE)
}

static VTABLE: RawWakerVTable = RawWakerVTable::new(|_| noop_raw_waker(), |_| {}, |_| {}, |_| {});
use std::future::Future;
use std::pin::pin;
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};

fn main() {
    let bonus = 2;
    let future = async {
        let left = ready_value(10).await;
        let right = ready_value(bonus).await;
        left + right
    };
    let total = block_on(future);
    println!("{total}");
}

async fn ready_value(value: i32) -> i32 {
    value
}

fn block_on<F: Future>(future: F) -> F::Output {
    let waker = noop_waker();
    let mut context = Context::from_waker(&waker);
    let mut future = pin!(future);
    loop {
        match future.as_mut().poll(&mut context) {
            Poll::Ready(value) => return value,
            Poll::Pending => {}
        }
    }
}

fn noop_waker() -> Waker {
    unsafe { Waker::from_raw(noop_raw_waker()) }
}

fn noop_raw_waker() -> RawWaker {
    RawWaker::new(std::ptr::null(), &VTABLE)
}

static VTABLE: RawWakerVTable = RawWakerVTable::new(|_| noop_raw_waker(), |_| {}, |_| {}, |_| {});
use std::future::Future;
use std::pin::pin;
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};

fn main() {
    let bonus = 8;
    let future = async {
        let left = ready_value(10).await;
        let right = ready_value(bonus).await;
        left + right
    };
    let total = block_on(future);
    println!("{total}");
}

async fn ready_value(value: i32) -> i32 {
    value
}

fn block_on<F: Future>(future: F) -> F::Output {
    let waker = noop_waker();
    let mut context = Context::from_waker(&waker);
    let mut future = pin!(future);
    loop {
        match future.as_mut().poll(&mut context) {
            Poll::Ready(value) => return value,
            Poll::Pending => {}
        }
    }
}

fn noop_waker() -> Waker {
    unsafe { Waker::from_raw(noop_raw_waker()) }
}

fn noop_raw_waker() -> RawWaker {
    RawWaker::new(std::ptr::null(), &VTABLE)
}

static VTABLE: RawWakerVTable = RawWakerVTable::new(|_| noop_raw_waker(), |_| {}, |_| {}, |_| {});
  1. bonus ← 5, future ← (empty)

    5fn main() {6    let bonu→ 5s = 5; //@bonus=5, 2, 87    let futur→ (empty)e = async {8        let left = ready_value(10).await;9        let right = ready_value(bonus).await;10        left + right11    };12    let total = block_on(futur(empty)e);13    println!("{total}");
  2. waker ← Waker { data: 0x0, vtable: ⟨addr A⟩ }, context ← Context { waker: Waker { data: 0x0, vtable: ⟨addr A⟩ } }

    20fn block_on<F: Future>(future: F) -> F::Output {21    let wake→ Waker { data: 0x0, vtable: ⟨addr A⟩ }r = noop_waker();22    let mut contex→ Context { waker: Waker { data: 0x0, vtable: ⟨addr A⟩ } }t = Context::from_waker(&wakeWaker { data: 0x0, vtable: ⟨addr A⟩ }r);23    let mut futur→ (empty)e = pin!(future);24    loop {
  3. fn ready_value(value: i32) -> i32

    pass 1 of 2
    16async fn ready_value(value: i32) -> i32 {17    valu10e18}
  4. fn ready_value(value: i32) -> i32

    pass 2 of 2
    16async fn ready_value(value: i32) -> i32 {17    valu5e18}
  5. total ← 15

    11    };12    let tota→ 15l = block_on(futur(empty)e);13    println!("{total}");14}
    output15
  1. bonus ← 2, future ← (empty)

    5fn main() {6    let bonu→ 2s = 2;7    let futur→ (empty)e = async {8        let left = ready_value(10).await;9        let right = ready_value(bonus).await;10        left + right11    };12    let total = block_on(futur(empty)e);13    println!("{total}");
  2. waker ← Waker { data: 0x0, vtable: ⟨addr A⟩ }, context ← Context { waker: Waker { data: 0x0, vtable: ⟨addr A⟩ } }

    20fn block_on<F: Future>(future: F) -> F::Output {21    let wake→ Waker { data: 0x0, vtable: ⟨addr A⟩ }r = noop_waker();22    let mut contex→ Context { waker: Waker { data: 0x0, vtable: ⟨addr A⟩ } }t = Context::from_waker(&wakeWaker { data: 0x0, vtable: ⟨addr A⟩ }r);23    let mut futur→ (empty)e = pin!(future);24    loop {
  3. fn ready_value(value: i32) -> i32

    pass 1 of 2
    16async fn ready_value(value: i32) -> i32 {17    valu10e18}
  4. fn ready_value(value: i32) -> i32

    pass 2 of 2
    16async fn ready_value(value: i32) -> i32 {17    valu2e18}
  5. total ← 12

    11    };12    let tota→ 12l = block_on(futur(empty)e);13    println!("{total}");14}
    output12
  1. bonus ← 8, future ← (empty)

    5fn main() {6    let bonu→ 8s = 8;7    let futur→ (empty)e = async {8        let left = ready_value(10).await;9        let right = ready_value(bonus).await;10        left + right11    };12    let total = block_on(futur(empty)e);13    println!("{total}");
  2. waker ← Waker { data: 0x0, vtable: ⟨addr A⟩ }, context ← Context { waker: Waker { data: 0x0, vtable: ⟨addr A⟩ } }

    20fn block_on<F: Future>(future: F) -> F::Output {21    let wake→ Waker { data: 0x0, vtable: ⟨addr A⟩ }r = noop_waker();22    let mut contex→ Context { waker: Waker { data: 0x0, vtable: ⟨addr A⟩ } }t = Context::from_waker(&wakeWaker { data: 0x0, vtable: ⟨addr A⟩ }r);23    let mut futur→ (empty)e = pin!(future);24    loop {
  3. fn ready_value(value: i32) -> i32

    pass 1 of 2
    16async fn ready_value(value: i32) -> i32 {17    valu10e18}
  4. fn ready_value(value: i32) -> i32

    pass 2 of 2
    16async fn ready_value(value: i32) -> i32 {17    valu8e18}
  5. total ← 18

    11    };12    let tota→ 18l = block_on(futur(empty)e);13    println!("{total}");14}
    output18
async block `async { ... }` builds a future without naming a separate async function.
capture The async block can read `bonus` from the surrounding scope.
await sequence Each `.await` returns a value before the next expression uses it.