On this coming Thursday, November 7, async-await syntax hits stable Rust, as part of the 1.39.0 release. This work has been a long time in development -- the key ideas for zero-cost futures, for example, were first proposed by Aaron Turon and Alex Crichton in 2016! -- and we are very proud of the end result! We believe that Async I/O -- and async-await in particular -- is going to be an increasingly important part of Rust's story.
While this first release of "async-await" is a momentous event, it's also only the beginning. The current support for async-await marks a kind of "Minimum Viable Product" (MVP), and we expect to be polishing, improving, and extending it for some time.
Already, in the time since async-await hit beta, we've made a lot of great progress -- including making some key diagnostic improvements that help to make async-await errors far more approachable. If you'd like to get involved in that work, check out the Async Foundations Working Group -- if nothing else, you can help us by filing bugs about polish issues or by nominating those bugs that are bothering you the most, to help direct our efforts.
Now that async-await is approaching stabilization, all the major Async I/O runtimes are busily at work adding and extending their support for the new syntax:
- the tokio runtime recently announced a number of scheduler improvements, and they are planning a stable release in November that supports async-await syntax;
- the async-std runtime has been putting out weekly releases for the past months, and plans to make their 1.0 release shortly after async-await hits stable;
- using wasm-bindgen-futures, you can even bridge Rust Futures with [JavaScript promises];
- the hyper library has migrated to adopt standard Rust futures;
- the 0.3.0 version of the futures-rs library will support async-await and will be released at or around the time async-await hits stable (you can use the 0.3.0-alpha releases now);
- finally, async-await support is starting to become available in higher-level web frameworks as well.
(This section and the next are reproduced from the "Async-await hits beta!" post.)
So, what is async await? Async-await is a way to write functions that can "pause", return control to the runtime, and then pick up from where they left off. Typically those pauses are to wait for I/O, but there can be any number of uses.
You may be familiar with the async-await from other languages, such as JavaScript or C#. Rust's version of the feature is similar, but with a few key differences.
To use async-await, you start by writing async fn
instead of fn
:
async fn first_function() -> u32 { .. }
Unlike a regular function, calling an async fn
doesn't do anything
to start -- instead, it returns a Future
. This is a suspended
computation that is waiting to be executed. To actually execute
the future, you have to use the .await
operator:
async fn another_function() {
// Create the future:
let future = first_function();
// Await the future, which will execute it (and suspend
// this function if we encounter a need to wait for I/O):
let result: u32 = future.await;
...
}
This example shows the first difference between Rust and other
languages: we write future.await
instead of await future
. This
syntax integrates better with Rust's ?
operator for propagating
errors (which, after all, are very common in I/O). One can simply
write future.await?
to await the result of a future and propagate
errors. It also has the advantage of making method chaining painless.
The other difference between Rust futures and futures in other languages is that they are based on a "poll" model, which makes them zero cost. In other languages, invoking an async function immediately creates a future and schedules it for execution: awaiting the future isn't really necessary for it to execute. But this implies some overhead for each future that is created.
In contrast, in Rust, calling an async function does not do any scheduling in and of itself, which means that we can compose a complex nest of futures without incurring a per-future cost. As an end-user, though, the main thing you'll notice is that futures feel "lazy": they don't do anything until you await them.
If you'd like a closer look at how futures work under the hood, take a look at the executor section of the async book, or watch the excellent talk that withoutboats gave at Rust LATAM 2019 on the topic.
We believe that having async-await on stable Rust is going to be a key enabler for a lot of new and exciting developments in Rust. If you've tried Async I/O in Rust in the past and had problems -- particularly if you tried the combinator-based futures of the past -- you'll find async-await integrates much better with Rust's borrowing system). Moreover, there are a now a number of great runtimes and other libraries available in the ecosystem to work with. So get out there and build stuff!