.await
is a keyword, thus asking how it is defined is equivalent to asking how if
or match
are defined. The true answer is an unsatisfying one: look at the code of the Rust compiler.
The async/await RFC describes it as:
The await!
compiler built-in
A builtin called await!
is added to the compiler. await!
can be used to
"pause" the computation of the future, yielding control back to the caller.
await!
takes any expression which implements IntoFuture
, and evaluates to a
value of the item type that that future has.
// future: impl Future<Output = usize>
let n = await!(future);
The expansion of await repeatedly calls poll
on the future it receives,
yielding control of the function when it returns Poll::Pending
and
eventually evaluating to the item value when it returns Poll::Ready
.
await!
can only be used inside of an async function, closure, or block.
Using it outside of that context is an error.
(await!
is a compiler built-in to leave space for deciding its exact syntax
later. See more information in the unresolved questions section.)
The expansion of await
The await!
builtin expands roughly to this:
let mut future = IntoFuture::into_future($expression);
let mut pin = unsafe { Pin::new_unchecked(&mut future) };
loop {
match Future::poll(Pin::borrow(&mut pin), &mut ctx) {
Poll::Ready(item) => break item,
Poll::Pending => yield,
}
}
This is not a literal expansion, because the yield
concept cannot be
expressed in the surface syntax within async
functions. This is why await!
is a compiler builtin instead of an actual macro.
Note that the syntax await!(expression)
was changed to expression.await
before stabilization.
Does the compiler have magical knowledge of the Future
trait
Yes. Future
is a language item.
can x
be anything that implements IntoFuture
or is it more complicated?
There isn't actually an IntoFuture
in the standard library; I assume that the level of indirection wasn't needed during implementation. .await
only works with types implementing Future
.
See also: