Async Concepts
Async Block
Build a Future Inline
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.
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(), |_| {}, |_| {}, |_| {});
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}");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 {fn ready_value(value: i32) -> i32
pass 1 of 216async fn ready_value(value: i32) -> i32 {17 valu10e18}fn ready_value(value: i32) -> i32
pass 2 of 216async fn ready_value(value: i32) -> i32 {17 valu5e18}total ← 15
11 };12 let tota→ 15l = block_on(futur(empty)e);13 println!("{total}");14}output15
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}");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 {fn ready_value(value: i32) -> i32
pass 1 of 216async fn ready_value(value: i32) -> i32 {17 valu10e18}fn ready_value(value: i32) -> i32
pass 2 of 216async fn ready_value(value: i32) -> i32 {17 valu2e18}total ← 12
11 };12 let tota→ 12l = block_on(futur(empty)e);13 println!("{total}");14}output12
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}");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 {fn ready_value(value: i32) -> i32
pass 1 of 216async fn ready_value(value: i32) -> i32 {17 valu10e18}fn ready_value(value: i32) -> i32
pass 2 of 216async fn ready_value(value: i32) -> i32 {17 valu8e18}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.