Asynchrony (Scoped Effect)
Description
Asynchrony in Rust enables non-blocking operations to be authored in an imperative fashion. This can be helpful for performance reasons, but feature-wise it enables "arbitrary concurrency" and "arbitrary cancellation" of computations. These can in turn be composed and leveraged by higher-level control-flow primitives such as "arbitrary timeouts" and "arbitrary parallel execution".
Asynchrony in Rust is implemented using a pair of keywords. async is used to
create an async context which is reified into a state machine backed by the
Future trait. And .await is used on the call-site to access the values
inside of an async context. Because .await can only be called inside of
async contexts, it eventually needs to be consumed by a top-level function
which knows how to run a future to completion.
Feature Status
async/.await in Rust is considered "MVP stable". This means the reification of
the effect is stable, and both the async and .await keywords exist in the
language, but not all keyword positions are available yet.
Feature categorization
| Position | Syntax |
|---|---|
| Effect | async |
| Yield | N/A |
| Apply | .await |
| Consume | thread::block_on †, async fn main ‡ |
| Reification | impl Future |
†
thread::block_onis not yet part of the stdlib, and only exists as a library feature. An example implementation can be found in theWakedocs.
‡
async fn mainis not yet part of the language, and only exists as a proc-macro extension as part of the ecosystem. It chiefly wraps the existingfn mainlogic in athread::block_oncall.
Positions Available
| Position | Available | Example |
|---|---|---|
| Manual trait impl | ✅ | impl Future for Cat {} |
| Free functions | ✅ | async fn meow() {} |
| Inherent functions | ✅ | impl Cat { async fn meow() {} } |
| Trait methods | ⏳ | trait Cat { async fn meow() {} } |
| Trait declarations | ❌ | async trait Cat {} |
| Block scope | ✅ | fn meow() { async {} } |
| Argument qualifiers | ❌ | fn meow(cat: impl async Cat) {} |
| Data types † | ❌ | async struct Cat {} |
| Drop † | ❌ | impl async Drop for Cat {} |
| Closures | ❌ | async ǀǀ {} |
| Iterators | ❌ | for await cat in cats {} |
† In non-async Rust if you place a value which implements
Dropinside of another type, the destructor of that value is run when the enclosing type is destructed. This is called drop-forwarding. In order for drop-forwarding to work with async drop, some form of "async value" notation will be required.
Refinements
| Modifier | Description |
|---|---|
| cancellation-safe | Has no associated future-local state |
Cancellation-Safe Futures
"cancellation-safety" is currently more like a term of art than an first-class term. It is a property used and relied upon by ecosystem APIs, but it is not represented in the type system anywhere. Which means APIs which rely on "cancellation-safety" do so without compiler-backing, which makes them a notorious source of bugs. This should probably be fixed, and when we do we probably will not want to call it "cancellation-safety" since it relates less to "cancellation" and more to the statefulness of futures, and whether or not they can be recreated without side-effects or data loss.
Fused Futures
A FusedFuture super-trait also exists, but it does not meaningfully feel like
a modifier of the "async" effect. It only adds an is_terminated method which
returns a bool. It does not inherently change the semantic functioning of the
underlying Iterator trait, or enhance it with behavior which is otherwise
absent. This is different from e.g. FusedIterator which says something about
the behavior of the Iterator::next function.
It's also worth noting that the FusedFuture trait is mostly useful for the
select! control-flow construct. Without that, FusedFuture would likely not
see much use
(ref).