# Async Rust in 2023 and beyond The Async Rust working group has been revisiting our roadmap for the next few years. We'd like to share our plan for what we'll accomplish in 2023, 2024, and beyond. ## Scope of the roadmap First, we have sharpened the scope of the roadmap to only include language and standard library features. Tooling and compiler-based improvements are still part of our overall _vision_ for async, but their dependency graphs look very different. We're more confident in our ability to drive this narrower scope of features and deliver them on a given timeline. We think it would also be great to have others step up and drive the other areas. ## Tenets These are the criteria and beliefs that we used to form this roadmap. They are ordered such that, if in conflict, we give priority to the first one. **Steady progress.** People are building foundational layers in async Rust *now*. We need to ship incrementally, building our way to the end goal, but improving stable Rust along the way. This means maintaining a razor-sharp focus, even as we consider how later developments might interact with our current goals. **Stay ambitious.** We need to continue supporting the async Rust code that exists today, but we shouldn't be held back by it. The current experience isn't where we want it to be, and we shouldn't be afraid to propose and make ambitious changes, as long as there's a path from here to there. **Enable the ecosystem**. The primary role of the Rust org is to build the interconnections that allow the ecosystem to scale and interoperate. Sometimes standardizing one layer simplifies further development atop it, allowing for a flourishing ecosystem. **Drive consistency.** The async Rust experience right now varies dramatically depending on your domain, and getting started involves a lot of decisions with non-obvious and long-lasting implications. We need to find ways to make async Rust more uniform. See also the [Rustacean design principles][rdp] (Reliability, Performant, Supportive, Productive, Transparent, Versatile, in that order). The Async Rust experience as it stands now arguably puts Versatility above Reliability, Supportiveness, and Productivity through constructs like `select!` and `FuturesUnordered` as well as the lack of portable libraries and idioms that work uniformly across runtimes. [rdp]: https://rustacean-principles.netlify.app/how_rust_empowers.html ## Overview ![layer diagram](https://i.imgur.com/2HmLIDe.png) _Also available in [table form](https://github.com/orgs/rust-lang/projects/28/views/1)._ Generally speaking, each item in this diagram depends on the items beneath it. Solid lines are for things we know we want, dashed lines represent stepping stones, and dotted lines are for things we aren't sure about yet. <!-- | | 2023 | 2024 | 2027 (provisional) | | --- | --- | --- | --- | | "Async fn everywhere" | Async fn in traits (AFIT) MVP | Dynamic dispatch for AFIT, async closures | Async drop | | Portability across runtimes | | Basic traits | The high-level roadmap for the next few years is as follows --> ### 2023: Async fn in trait This year we will stabilize the MVP for async fn in trait. This includes: * `async fn` in trait * `-> impl Trait` in trait * Simple `Send` bounds Whatever combination of features we end up with, they must be sufficient for 3 or more representative use cases to publish stable APIs with. Some less-ergonomic workarounds may be required, as long as fixing them won't require breaking APIs later. * Customizable providers in AWS SDK * Tower service traits * Async on embedded with Embassy * *maybe others* The MVP does _not_ include dynamic dispatch or "complex" cases of Send bounds. In order to ship this year, we'll need a stabilization PR to land sometime in early Q3. This means we need to be firming up open questions and driving to consensus now. ### 2024: Async everywhere By the end of 2024, async will have an equal standing in Rust's most commonly used abstraction mechanisms. This should make async feel like an integrated part of the language, supercharging _productivity_. First we want to stabilize an `async Iterator` trait, including combinators like `map`. This involves solving real-world problems with async, captures, and generic code, particularly async closures. Next, round out the `async fn` and RPITIT stories by making dynamic dispatch work using `dyn Trait`. This should remove all use cases for the `async-trait` crate. Finally, make it possible to publish portable implementations of protocols using official traits for runtime interop and basic I/O. Representative use cases are `hyper` and `quinn`. 2024 will be packed, with a lot of exciting possibilties opening up for the ecosystem. If everything goes according to plan this year, we should have most of the second half of 2023 to work on these features, in addition to the first half of 2024. ### 2027: Everything is awesome With many of the ecosystem's most fundamental limitations out of the way, we'd like to shift our focus making async Rust more _reliable_ and _supportive_. We expect reliability improvements to come mainly through new patterns for managing concurrency ("Combinators 2.0" and structured concurrency), along with a mechanism for async destruction. The fundamental work on abstraction mechanisms like async closures will enable this. It will also be a key thing to look forward to as we work on a `Spawn` trait in 2024. We will support async Rust users by making it much easier to get started with a notion of a global executor for use with `async fn main`, a built-in default runtime, and async APIs for files and sockets in the standard library. We will continue making it possible to customize your runtime while using these features. Finally, we will deliver further productivity improvements through await patterns (`for await`). We're also interested in [async] generators and keyword generics, but tend to see those as separate initiatives. These features are far enough in the future that many details may change. We expect them to come more into focus over the next year or two. Around this time next year (end of Q1 2024) we will add a 2025 milestone and pull some of these features into it. ## Alternatives ### Reliability in 2024 After shipping fundamental abstraction features like async closures and dyn Trait, we can shift our focus directly to the reliability question and start publishing our new opinionated patterns for use in the broader ecosystem. That would mean de-prioritizing productivity and interop. In terms of the tenets, this would place ambition and consistency over continuous progress and enabling the ecosystem. It's true that reliability is one of the most _significant_ problems async Rust faces today, compared to synchronous Rust, and it should be given high priority. It's also likely to be one of the tougher problems to crack, given the diversity of use cases and the many challenges of language design involved. And given its dependence on async closures, there might not be enough iteration time to ship both of these in the same year. On the other hand, we think we have a pretty good handle on what it would take to ship basic I/O traits. Spawn and timer traits require taking an opinionated stance, but only in a pretty narrow sense of whether we can express a structured concurrency paradigm using them, and that doesn't seem too hard to accommodate. Finally, `async Iterator` is a clear goal that requires us to exercise our generic programming muscles and may also itself be useful in building them. There's a chance some of this experimentaion will happen anyway in the meantime. We should do what we can to encourage experimentation with crates like [`futures_concurrency`](https://docs.rs/futures-concurrency/latest/futures_concurrency/). One of the ways we'll do that, of course, is by shipping core abstraction mechanisms. We should then revisit this question when planning for 2025. ### Generators in 2024 There's been some recent debate over whether `async fn next` is the right core primitive for async iteration, rather than `poll_next`. It would be easier to write an async `next` implementation, but the fact that async functions must return a separate object leads to irregularities in how the async version of the trait would work: * You can't store a type implementing `async Iterator` and the future returned by `next()` in the same struct, because one borrows from the other. * `dyn async Iterator` always requires boxing or an equivalent adapter. If we did adopt `poll_next` we would almost certainly want to prioritize generators and async generators, so these could easily be written. This does have a lot of benefits and reduces the need for combinators. Alternatively, we could come up an easy way to still write a `next()`-like implementation as an async function. But it's debated whether this would actually make user's lives much easier, since you are still having to write a state machine by hand. If we did prioritize async generators, we would have to pick something to deprioritize. Maybe portable protocols, since that's less "fundamental" and we want to keep `dyn` to finally finish AFIT. Or maybe we bump `dyn`, because it becomes less important in a world where `async Iterator` is already dyn-safe. ## Tactical planning ![Timeline view](https://i.imgur.com/5lqLkiq.png) _source: [Github project](https://github.com/orgs/rust-lang/projects/28/views/2)_ * By Apr 1: Prepare write-ups for Send bounds * Min RTN (Niko will champion) * Min trait transformers (Anyone want to champion?) * Noncommittal deriver? * By end of Apr: * accepted RFC for one of these three approaches * plus implemented * May * ... * June * ... * By July: * stabilized