.await
The .await postfix operator (expr.await, usable inside async fn and async blocks) drives a Future (or any type implementing std::future::IntoFuture) to completion by polling it, yielding Poll::Pending back to the caller's executor whenever the inner future is not yet ready, and evaluating to Future::Output once Poll::Ready(val) is returned.
Why .await Uses Postfix Syntax (fut.await?...)
Rust designed .await as a postfix field-like operator rather than a prefix keyword (await fut) so it chains naturally left-to-right with method calls and the ? error-propagation operator without parentheses:
Prefix Syntax in Other Languages (Requires Nested Parentheses):
let text = await (await (await client.get(url)).send()).text();
Rust Postfix `.await` + `?` Chaining (Clean Left-to-Right Pipeline):
let text = client
.get(url)
.send()
.await?
.text()
.await?;
How expr.await Desugars Under the Hood
When you write sub_future.await inside an async block, the compiler lowers it to a loop that converts sub_future via IntoFuture::into_future, pins it as a state-machine field, and polls it using the caller's Context<'_>:
Conceptual Desugaring of `let result = expr.await;`:
let mut pinned_sub = IntoFuture::into_future(expr);
loop {
match Future::poll(Pin::new_unchecked(&mut pinned_sub), cx) {
Poll::Ready(val) => break val,
Poll::Pending => {
// Save current state-machine variant and return `Poll::Pending`
// up to the executor! When woken, resume at the top of this loop!
yield Poll::Pending;
}
}
}
Custom Types That Can Be .awaited via std::future::IntoFuture
Stabilized in Rust 1.64, .await does not require its operand to implement Future directly—it requires std::future::IntoFuture (which has a blanket implementation for all F: Future!).
Implementing IntoFuture on a request builder struct allows users to configure the builder and then .await the builder directly without calling an extra .send() or .build() method first:
use std::future::{Future, IntoFuture, Ready, ready};
use std::pin::pin;
use std::task::{Context, Poll, Waker};
struct QueryBuilder {
table: &'static str,
limit: usize,
}
impl QueryBuilder {
fn new(table: &'static str) -> Self {
Self { table, limit: 10 }
}
fn limit(mut self, limit: usize) -> Self {
self.limit = limit;
self
}
}
// Implementing `IntoFuture` allows `.await`ing `QueryBuilder` directly!
impl IntoFuture for QueryBuilder {
type Output = String;
type IntoFuture = Ready<String>;
fn into_future(self) -> Self::IntoFuture {
ready(format!("SELECT * FROM {} LIMIT {}", self.table, self.limit))
}
}
async fn run_query() -> String {
// `.await` invokes `<QueryBuilder as IntoFuture>::into_future` automatically:
QueryBuilder::new("orders").limit(25).await
}
fn main() {
let mut fut = pin!(run_query());
let mut cx = Context::from_waker(Waker::noop());
if let Poll::Ready(sql) = fut.as_mut().poll(&mut cx) {
println!("executed via IntoFuture: {}", sql);
}
}
executed via IntoFuture: SELECT * FROM orders LIMIT 25