├── .gitignore ├── evaluation ├── scenarios │ ├── dyn.md │ ├── dyn │ │ ├── embedded-async-drop.md │ │ ├── inner-loop.md │ │ ├── async-drop.md │ │ ├── taking-ownership.md │ │ └── embedded-consume.md │ ├── implementing-async-read.md │ ├── async-fn-in-traits.md │ └── background-logging.md ├── challenges.md ├── scenarios.md ├── challenges │ ├── bounding_futures.md │ ├── naming_futures.md │ ├── guaranteeing_async_drop.md │ ├── bounding_async_drop.md │ ├── implicit_await_with_async_drop.md │ └── dyn_traits.md ├── design │ ├── dynx.md │ ├── auto_traits_consider_async_drop.md │ ├── simple_names.md │ ├── dynx │ │ ├── auto_trait.md │ │ ├── creation.md │ │ └── sealed_traits.md │ ├── trait_multiplication.md │ ├── bound_items.md │ ├── with_clauses.md │ ├── custom_dyn_impls.md │ ├── implied_send.md │ └── inline_async_fn.md ├── case-studies.md ├── design.md ├── executor-styles.md └── case-studies │ ├── embassy.md │ ├── builder-provider-api.md │ ├── socket-handler.md │ ├── microsoft.md │ └── tower.md ├── explainer ├── implementation_plan.md ├── async_fn_in_traits.md ├── user_facing_summary.md ├── async_fn_in_dyn_trait │ ├── future_possibilities.md │ ├── how_it_feels.md │ ├── unresolved.md │ ├── how_it_works.md │ ├── identity_shim_functions.md │ ├── hardcoding_box.md │ ├── avoiding_allocation.md │ ├── nested_impl_trait.md │ └── generalizing_from_box_to_dynx.md ├── inline_async_iter_adapter.md └── async_fn_in_dyn_trait.md ├── meetings └── README.md ├── archive.md ├── updates.md ├── CODE_OF_CONDUCT.md ├── .github ├── ISSUE_TEMPLATE │ ├── question.md │ ├── proposal.md │ ├── bug-report.md │ └── experience-report.md └── workflows │ └── mdbook.yml ├── RFC └── static_async_fn_in_traits.md ├── FAQ.md ├── RFC.md ├── explainer.md ├── badges.md ├── LICENSE-MIT ├── book.toml ├── roadmap ├── static_async_trait.md ├── impl_trait_in_traits.md ├── dyn_trait.md ├── async_closures.md ├── dyn_async_trait.md ├── static_async_trait_mvp.md └── async_drop.md ├── evaluation.md ├── CHARTER.md ├── updates └── 2021-oct.md ├── stakeholders.md ├── roadmap.md ├── README.md ├── SUMMARY.md ├── archive └── 2021-phase_1_narrative.md └── LICENSE-APACHE /.gitignore: -------------------------------------------------------------------------------- 1 | /book 2 | -------------------------------------------------------------------------------- /evaluation/scenarios/dyn.md: -------------------------------------------------------------------------------- 1 | # Dyn 2 | -------------------------------------------------------------------------------- /evaluation/scenarios/dyn/embedded-async-drop.md: -------------------------------------------------------------------------------- 1 | # Embedded async drop 2 | -------------------------------------------------------------------------------- /evaluation/scenarios/dyn/inner-loop.md: -------------------------------------------------------------------------------- 1 | # Performance-sensitive inner loop with dynamic dispatch 2 | -------------------------------------------------------------------------------- /evaluation/challenges.md: -------------------------------------------------------------------------------- 1 | # Challenges 2 | 3 | This section describes the *challenges* that we need to find solutions for. 4 | 5 | -------------------------------------------------------------------------------- /explainer/implementation_plan.md: -------------------------------------------------------------------------------- 1 | # Appendix: Implementation plan 2 | 3 | ![planning rfc][] 4 | 5 | {{#include ../badges.md}} 6 | -------------------------------------------------------------------------------- /meetings/README.md: -------------------------------------------------------------------------------- 1 | # Meetings 2 | 3 | This folder contains the minutes of all the recorded meetings that have happened 4 | so far. 5 | -------------------------------------------------------------------------------- /archive.md: -------------------------------------------------------------------------------- 1 | The "archive" stores older documents that are preserved for historical purposes but which have been updated with newer designs etc. -------------------------------------------------------------------------------- /updates.md: -------------------------------------------------------------------------------- 1 | # ✏️ Updates 2 | 3 | Lang-team initiatives give monthly updates. This section collects the updates from this initiative for posterity. -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # The Rust Code of Conduct 2 | 3 | The Code of Conduct for this repository [can be found online](https://www.rust-lang.org/conduct.html). 4 | -------------------------------------------------------------------------------- /explainer/async_fn_in_traits.md: -------------------------------------------------------------------------------- 1 | # Async fn in traits 2 | 3 | ![accepted rfc][] 4 | 5 | {{#include ../badges.md}} 6 | 7 | See [the RFC](../RFC/static_async_fn_in_traits.md). -------------------------------------------------------------------------------- /evaluation/scenarios.md: -------------------------------------------------------------------------------- 1 | # Reference scenarios 2 | 3 | - [Logging in the background](./scenarios/background-logging.md) 4 | - [Implementing AsyncRead](./scenarios/implementing-async-read.md) 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Ask for clarification about some aspect of the design 4 | title: '' 5 | labels: question 6 | assignees: '' 7 | 8 | --- 9 | 10 | Please describe what you want to know! -------------------------------------------------------------------------------- /evaluation/challenges/bounding_futures.md: -------------------------------------------------------------------------------- 1 | # Bounding futures 2 | 3 | The challenge is to be able to concisely and intuitively bound the *futures resulting from async fn calls*, as described in the [Background Logging](../scenarios/background-logging.md) scenario. -------------------------------------------------------------------------------- /evaluation/scenarios/dyn/async-drop.md: -------------------------------------------------------------------------------- 1 | # Async drop 2 | 3 | ## Summary 4 | 5 | * Able to define an `async fn drop` for types that must release async resources when they go out of scope 6 | * Manage the interaction with `Send` 7 | 8 | ## Status quo 9 | 10 | 11 | 12 | ## Shiny future -------------------------------------------------------------------------------- /RFC/static_async_fn_in_traits.md: -------------------------------------------------------------------------------- 1 | # Approved RFC: Static async fn in traits 2 | 3 | This RFC was opened on the Rust RFCs repository as [RFC#3185] and was merged. The tracking issue is [#91611]. 4 | 5 | [RFC#3185]: https://github.com/rust-lang/rfcs/pull/3185 6 | [#91611]: https://github.com/rust-lang/rust/issues/91611 7 | -------------------------------------------------------------------------------- /evaluation/design/dynx.md: -------------------------------------------------------------------------------- 1 | # Dynx 2 | 3 | A `dynx Trait` represents "a pointer to something that implements `Trait`". It is an implementation detail of our design for "async fn in traits". Best description is currently to be found in [the explainer](../../explainer/async_fn_in_dyn_trait.md). This area covers various thorny subquestions. -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # 😕 Frequently asked questions 2 | 3 | > This page lists frequently asked questions about the design. It often redirects to the other pages on the site. 4 | 5 | ## What is the goal of this initiative? 6 | 7 | See the [Charter](./CHARTER.md). 8 | 9 | ## Who is working on it! 10 | 11 | See the [Charter](./CHARTER.md). 12 | -------------------------------------------------------------------------------- /evaluation/design/auto_traits_consider_async_drop.md: -------------------------------------------------------------------------------- 1 | # Auto traits consider async drop 2 | 3 | One way to solve the [bounding async drop] challenge is to require that, if a type `X` implements `AsyncDrop`, then `X: Send` only if the type of its async drop future is also `Send`. The drop trait is already integrated quite deeply into the language, so adding a rule like this would not be particularly challenging. 4 | 5 | [bounding async drop]: ../challenges/bounding_async_drop.md -------------------------------------------------------------------------------- /RFC.md: -------------------------------------------------------------------------------- 1 | # ✨ RFC 2 | 3 | > The RFC exists here in draft form. It will be edited and amended over the course of this initiative. 4 | > Note that some initiatives produce multiple RFCs. 5 | > 6 | > Until there is an accepted RFC, any feature gates must be labeled as experimental. 7 | > 8 | > When you're ready to start drafting, copy in the [template text](https://raw.githubusercontent.com/rust-lang/rfcs/master/0000-template.md) from the [rfcs](https://github.com/rust-lang/rfcs) repository. -------------------------------------------------------------------------------- /explainer.md: -------------------------------------------------------------------------------- 1 | # 📚 Explainer 2 | 3 | > The "explainer" is "end-user readable" documentation that explains how to use the feature being deveoped by this initiative. 4 | > If you want to experiment with the feature, you've come to the right place. 5 | > Until the feature enters "feature complete" form, the explainer should be considered a work-in-progress. 6 | 7 | For an overview, start with the [user guide from the future](./explainer/user_guide_future.md). 8 | 9 | More detailed explanations of each feature follow. 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/proposal.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Proposal 3 | about: Propose a change to the way this feature works 4 | title: '' 5 | labels: proposal 6 | assignees: '' 7 | 8 | --- 9 | 10 | **When proposing changes, please check the FAQ and design questions to see if this idea has already been considered. It's also a good idea to check with the owner first.** 11 | 12 | ### Motivation 13 | 14 | Describe what you're trying to accomplish -- what problem are you trying to solve? 15 | 16 | ### Details 17 | 18 | Describe your idea. 19 | 20 | -------------------------------------------------------------------------------- /evaluation/scenarios/dyn/taking-ownership.md: -------------------------------------------------------------------------------- 1 | # Taking ownership of the receiver 2 | 3 | ## Summary 4 | 5 | * Support traits that use `fn(self)` with dynamic dispatch 6 | * The caller will have to be using a `Box`, but that is not hard-coded into the trait. 7 | 8 | ## Status quo 9 | 10 | Grace is working on an embedded system. She needs to parse data from an input stream that is formatted as a series of packets in the format TLA. She finds a library `tla` on crates.io with a type that implements the async iterator trait: 11 | -------------------------------------------------------------------------------- /badges.md: -------------------------------------------------------------------------------- 1 | [not started]: https://img.shields.io/badge/status-not%20started-informational 2 | [planning rfc]: https://img.shields.io/badge/status-draft%20rfc-informational 3 | [accepted rfc]: https://img.shields.io/badge/status-accepted%20rfc-informational 4 | [needs decision]: https://img.shields.io/badge/status-needs%20decision-informational 5 | [stable]: https://img.shields.io/badge/status-stable-success 6 | [nightly]: https://img.shields.io/badge/status-nightly-important 7 | [may-change]: https://img.shields.io/badge/warning-implementation%20may%20change-critical -------------------------------------------------------------------------------- /explainer/user_facing_summary.md: -------------------------------------------------------------------------------- 1 | # User-facing summary 2 | 3 | ![planning rfc][] 4 | 5 | {{#include ../badges.md}} 6 | 7 | This is a brief summary of the user-facing changes. 8 | 9 | * Extend the definition of `dyn Trait` to include: 10 | * Async functions 11 | * Functions that return `-> impl Trait` (note that `-> (impl Trait, impl Trait)` or other such constructions are not supported) 12 | * So long as `Trait` is dyn safe 13 | * Extend the definition of `impl` to permit 14 | * `#[dyn(box)]` and `#[dyn(identity)]` annotations 15 | * TBD 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report a bug using this feature 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | --- 8 | 9 | ### When using 10 | 11 | Please indicate the precise nightly build you are using; try `rustc --version` 12 | 13 | ### What I tried to do 14 | 15 | Please include sample code or links to your project! 16 | 17 | ### What happened 18 | 19 | Describe what happened in as much detail as possible. 20 | For compiler ICEs, a good idea would be to include the output with `RUST_BACKTRACE=1`. 21 | 22 | ### What I expected 23 | 24 | Describe what you thought should happen. 25 | -------------------------------------------------------------------------------- /evaluation/challenges/naming_futures.md: -------------------------------------------------------------------------------- 1 | # Naming futures 2 | 3 | ## Status 4 | 5 | Seems unlikely to be adopted. Doesn't *feel* right, and doesn't give any expanded abilities, such as the ability to name the future type. 6 | 7 | ## Summary 8 | 9 | The challenge is to be able to name the future that results from a particular async function. This can be used, for example, when naming future types, or perhaps as part of [bounding futures]. 10 | 11 | It is likely that this problem is best solved in the context of the [impl trait initiative]. 12 | 13 | [bounding futures]: ./bounding_futures.md 14 | [impl trait initiative]: https://rust-lang.github.io/impl-trait-initiative/ 15 | 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/experience-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Experience report 3 | about: Describe your experiences using this feature 4 | title: '' 5 | labels: experience-report 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Code description 11 | 12 | Describe what you were trying to do with generic associated types. Links to source code are always appreciated! 13 | 14 | ### What worked well 15 | 16 | Describe what aspects of GATs worked well for you. 17 | 18 | ### What worked less well 19 | 20 | Describe what aspects of GATs did not work well for you. Perhaps they were confusing, or you weren't able to get your code to compile the way you wanted? Or perhaps it was just not ergonomic. 21 | -------------------------------------------------------------------------------- /evaluation/design/simple_names.md: -------------------------------------------------------------------------------- 1 | # Simple names 2 | 3 | One simple way to give names to async functions is to just generate a name based on the method. For example: 4 | 5 | ```rust 6 | trait AsyncIterator { 7 | type Item; 8 | async fn next(&mut self) -> Self::Item; 9 | } 10 | ``` 11 | 12 | could desugar to 13 | 14 | ```rust 15 | trait AsyncIterator { 16 | type Item; 17 | type Next<'me>: Future + 'me; 18 | fn next(&mut self) -> Self::Next<'_>; 19 | } 20 | ``` 21 | 22 | Users could then name the future with `::Next<'a>`. 23 | 24 | This is a simple solution, but not a very general one, and perhaps a bit surprising (for example, there is no explicit declaration of `Next`). 25 | 26 | -------------------------------------------------------------------------------- /evaluation/challenges/guaranteeing_async_drop.md: -------------------------------------------------------------------------------- 1 | # Guaranteeing async drop 2 | 3 | One challenge with `AsyncDrop` is that we have no guarantee that it will be used. For any type `MyStruct` that implements `AsyncDrop`, it is always possible in Rust today to drop an instance of `MyStruct` in synchronous code. In that case, we cannot run the async drop. What should we do? 4 | 5 | Obvious alternatives: 6 | 7 | * Panic or abort 8 | * Use some form of "block on" or other default executor to execute the asynchronous await 9 | * Extend Rust in some way to prevent this condition. 10 | 11 | We can also mitigate this danger through lints (e.g., dropping value which implements AsyncDrop). 12 | 13 | Some types may implement both synchronous and asynchronous drop. -------------------------------------------------------------------------------- /evaluation/design/dynx/auto_trait.md: -------------------------------------------------------------------------------- 1 | # With auto traits 2 | 3 | 4 | We plan to address this in a follow-up RFC. The core idea is to build on the notation that one would use to express that you wish to have an async fn return a `Send`. As an example, one might write `AsyncIterator` to indicate that `next()` returns a `Send` future; when we generate the vtable for a `dyn AsyncIterator`, we can ensure that the bounds for `next` are applied to its return type, so that it would return a `dynx Future + Send` (and not just a `dynx Future`). We have also been exploring a more convenient shorthand for declaring "all the futures returned by methods in trait should be `Send`", but to avoid bikeshedding we'll avoid talking more about that in this document! -------------------------------------------------------------------------------- /evaluation/challenges/bounding_async_drop.md: -------------------------------------------------------------------------------- 1 | # Bounding async drop 2 | 3 | As a special case of the [bounding futures] problem, we must consider `AsyncDrop`. 4 | 5 | [bounding futures]: ./bounding_futures.md 6 | 7 | ```rust 8 | async fn foo(t: T) { 9 | runtime::sleep(22).await; 10 | } 11 | ``` 12 | 13 | The type of `foo(t)` is going to be a future type like `FooFuture`. This type will also include the types of all futures that get awaited (e.g., the return value of `runtime::sleep(22)` in this case). But in the case of `T`, we don't yet know what `T` is, and if it should happen to implement `AsyncDrop`, then there is an ["implicit await"](./implicit_await_with_async_drop.md) of that future. We have to ensure that the contents of that future are taken into account when we determine if `FooFuture: Send`. -------------------------------------------------------------------------------- /evaluation/case-studies.md: -------------------------------------------------------------------------------- 1 | # ⚖️ Case Studies 2 | 3 | *Spring 2023:* In preparation for stabilization an MVP of async function in traits, we asked a number of teams to integrate the nightly support on an experimental basis and talk through how well it worked for them. The following case studies provide details on their experiences. In short, the conclusions were: 4 | 5 | * Static dispatch AFIT generally meets expectations. 6 | * Most every project needs dynamic dispatch, but the workaround of deriving an "erased" version of the trait is sufficient for now, and can be hidden from end-users as a private impl detail. 7 | * Some solution for applying send bounds is required. RTN is fine for most cases but doesn't scale well to traits with many methods. 8 | * Having some automated way to derive "dynamic compatible" and "all methods with send bound" variants of the trait would be useful. -------------------------------------------------------------------------------- /evaluation/challenges/implicit_await_with_async_drop.md: -------------------------------------------------------------------------------- 1 | # Implicit await with async drop 2 | 3 | Consider this code: 4 | 5 | ```rust 6 | async fn foo(input: &QueryInput) -> anyhow::Result<()> { 7 | let db = DatabaseHandle::connect().await; 8 | let query = assemble_query(&input)?; 9 | let results = db.perform_query(query).await; 10 | while let Some(result) = results.next().await? { 11 | ... 12 | } 13 | } 14 | ``` 15 | 16 | Now let us assume that `DatabaseHandle` implements `AsyncDrop` to close the connection. There are numerous points here where `db` could be dropped (e.g., each use of `?`). At each of those points, there is effectively an implicit `await` similar to `AsyncDrop::async_drop(db).await`. It seems clear that users should not be required to manually write those things, but it is also a weakening of the existing `.await` contract (that all blocking points are visible). 17 | 18 | -------------------------------------------------------------------------------- /explainer/async_fn_in_dyn_trait/future_possibilities.md: -------------------------------------------------------------------------------- 1 | # Future possibilities 2 | 3 | ![planning rfc][] 4 | 5 | {{#include ../../badges.md}} 6 | 7 | ## Caching across calls 8 | 9 | It has been observed that, for many async fn, the function takes a `&mut self` "lock" on the `self` type. In that case, you can only ever have one active future at a time. Even if you are happy to box, you might get a big speedup by caching that box across calls (This is not clear, we'd have to measure, the allocator might do a better job than you can). 10 | 11 | We believe that it would be possible to 'upgrade' the ABI to perform this optimization without any change to user code: effectively the compiler would, when calling a function that returns `-> impl Trait` via `dyn`, allocate some "scratch space" on the stack and pass it to that function. The default generated shim, which today just boxes, would make use of this scratch space to stash the box in between calls (this would apply to any `&mut self` function, essentially). 12 | 13 | It's also possible to get this behavior today using an adapter. 14 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /evaluation/design.md: -------------------------------------------------------------------------------- 1 | # Design documents 2 | 3 | This section contains detailed design documents aimed at various challenges. 4 | 5 | | Document | Challenges addressed | Status | 6 | | -------------------------------- | -------------------------------------------- | ------ | 7 | | [Implied Send] | [Bounding futures] | ❌ | 8 | | [Trait multiplication] | [Bounding futures] | 🤔 | 9 | | [Inline async fn] | [Bounding futures], [Dyn traits] (partially) | 🤔 | 10 | | [Custom dyn impls] | [Dyn traits] | 🤔 | 11 | | [Auto traits consider AsyncDrop] | [Bounding drop] | 🤔 | 12 | 13 | [Implied Send]: ./design/implied_send.md 14 | [Trait multiplication]: ./design/trait_multiplication.md 15 | [Inline async fn]: ./design/inline_async_fn.md 16 | [Custom dyn impls]: ./design/custom_dyn_impls.md 17 | 18 | [Bounding futures]: ./challenges/bounding_futures.md 19 | [Dyn traits]: ./challenges/dyn_traits.md 20 | -------------------------------------------------------------------------------- /evaluation/scenarios/implementing-async-read.md: -------------------------------------------------------------------------------- 1 | # Implementing AsyncRead 2 | 3 | `AsyncRead` is being used here as a "stand-in" for some widely used trait that appears in the standard library. The details of the trait are not important. 4 | 5 | ## Self is send 6 | 7 | In this scenario, the Self type being used to implement is sendable, but the actual future that is created is not. 8 | 9 | ```rust 10 | struct MySocketBuddy { 11 | x: u32 12 | } 13 | 14 | impl AsyncRead for MySocketBuddy { 15 | async fn read(&mut self, buf: &mut [u8]) -> io::Result { 16 | let ssh_key = Rc::new(vec![....]); 17 | do_some_stuff(ssh_key.clone()); 18 | something_else(self.x).await; 19 | } 20 | // ERROR: `ssh_key` is live over an await; 21 | // Self implements Send 22 | // therefore resulting future must implement Send 23 | } 24 | ``` 25 | 26 | ## Self is not send 27 | 28 | In this scenario, the Self type being used to implement is not sendable. 29 | 30 | ```rust 31 | struct MySocketBuddy { 32 | x: u32, 33 | ssh_key: Rc>, 34 | } 35 | 36 | impl AsyncRead for MySocketBuddy { 37 | async fn read(&mut self, buf: &mut [u8]) -> io::Result { 38 | do_some_stuff(self.ssh_key.clone()); 39 | something_else(self.x).await; 40 | } 41 | // OK 42 | } 43 | ``` -------------------------------------------------------------------------------- /evaluation/executor-styles.md: -------------------------------------------------------------------------------- 1 | # Executor styles and Send bounds 2 | 3 | One key aspect of async fn in traits has to do with how to communicate `Send` bounds needed to spawn tasks. The key question is roughly "what send bounds are required to safely spawn a task?" 4 | 5 | * A **single threaded** executor runs all tasks on a single thread. 6 | * In this scenario, nothing has to be `Send`. 7 | * Example: [tokio::spawn_local](https://docs.rs/tokio/1.11.0/tokio/task/fn.spawn_local.html) 8 | * A **thread per core** executor selects a thread to run a task when the task is spawned, but never migrates tasks between threads. This can be very efficient because the runtimes never need to communicate across threads except to spawn new tasks. 9 | * In this scenario, the "initial state" must be `Send` but not the future once it begins executing. 10 | * Example: [glommio::spawn](https://docs.rs/glommio/0.5.1/glommio/struct.LocalExecutorBuilder.html#method.spawn) 11 | * A **work-stealing** executor can move tasks between threads even mid-execution. 12 | * In this scenario, the future must be `Send` at all times (or we have to rule out the ability to have leaks of data out from the future, which we don't have yet). 13 | * Example: [tokio::spawn](https://docs.rs/glommio/0.5.1/glommio/struct.LocalExecutorBuilder.html#method.spawn) 14 | -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["async fn fundamentals Members"] 3 | language = "en" 4 | multilingual = false 5 | src = "." 6 | title = "async fn fundamentals initiative" 7 | 8 | [output.html] 9 | no-section-label=true 10 | git-repository-url="https://github.com/rust-lang/async-fundamentals-initiative" 11 | edit-url-template="https://github.com/rust-lang/async-fundamentals-initiative/edit/master/{path}" 12 | site-url="/async-fundamentals-initiative/" 13 | 14 | [output.html.fold] 15 | enable = true 16 | level = 0 17 | 18 | # TODO: enable once actions are configured 19 | # [output.linkcheck] 20 | # optional = true 21 | 22 | [output.html.redirect] 23 | "/roadmap/mvp.html" = "/roadmap/static_async_trait_mvp.html" 24 | "/design-discussions/index.html" = "/roadmap.html" 25 | "/design-discussions/static_async_trait.html" = "/roadmap/static_async_trait.html" 26 | "/design-discussions/impl_trait_in_traits.html" = "/roadmap/impl_trait_in_traits.html" 27 | "/design-discussions/dyn_async_trait.html" = "/roadmap/dyn_async_trait.html" 28 | "/design-discussions/dyn_trait.html" = "/roadmap/dyn_trait.html" 29 | "/design-discussions/async_drop.html" = "/roadmap/async_drop.html" 30 | "/design-discussions/async_closures.html" = "/roadmap/async_closures.html" 31 | "/explainer/phase_1.html" = "/archive/2021-phase_1.html" 32 | "/explainer/phase_1_narrative.html" = "/archive/2021-phase_1_narrative.html" 33 | 34 | # Clean up yml headers from e.g. HackMD docs. 35 | [preprocessor.yml-header] 36 | -------------------------------------------------------------------------------- /roadmap/static_async_trait.md: -------------------------------------------------------------------------------- 1 | # Static async fn in traits 2 | 3 | ## Impact 4 | 5 | * Able to write `async fn` in traits and impls and use them in statically dispatched contexts 6 | * Able to easily declare that `T: Trait + Send` where "every async fn in `Trait` returns a `Send` future" 7 | 8 | ## Design notes 9 | 10 | Support async fn syntax in traits. 11 | 12 | The core idea is that it desugars into [impl trait in traits](./impl_trait_in_traits.md): 13 | 14 | ```rust 15 | trait SomeTrait { 16 | async fn foo(&mut self); 17 | } 18 | 19 | // becomes: 20 | 21 | trait SomeTrait { 22 | fn foo<(&mut self) -> impl Future + '_; 23 | } 24 | ``` 25 | 26 | Naturally it should also work in an impl: 27 | 28 | ```rust 29 | impl SomeTrait for someType { 30 | async fn foo(&mut self); 31 | } 32 | ``` 33 | 34 | For async functions in traits to be useful, it is important that traits containing `async fn` be dyn-safe, which introduces a number of challenges that we have to overcome. 35 | 36 | ## Frequently asked questions 37 | 38 | ### Can users easily bound those GATs with `Send`, maybe even in the trait definition? 39 | 40 | - People are likely to want to say "I want every future produced by this trait to be Send", and right now that is quite tedious. 41 | - We need a way to do this. 42 | - This applies equally to other "`-> impl Trait` in trait" scenarios. 43 | 44 | ### What about "dyn" traits? 45 | 46 | - See the sections on "inline" and "dyn" async fn in traits below! -------------------------------------------------------------------------------- /evaluation/scenarios/async-fn-in-traits.md: -------------------------------------------------------------------------------- 1 | # Async fn in traits usage scenarios 2 | 3 | What follows are a list of "usage scenarios" for async fns in traits and some of their characteristics. 4 | 5 | ## Core Rust async traits 6 | 7 | Defining core Rust async traits like `AsyncRead`: 8 | 9 | ```rust 10 | trait AsyncRead { 11 | async fn read(&mut self, buffer: &mut [u8]) -> usize; 12 | } 13 | ``` 14 | 15 | And implementing them in client code: 16 | 17 | ```rust 18 | ``` 19 | 20 | Characteristics: 21 | 22 | * Need static dispatch with zero overhead, so folks can write `fn foo()`, have it fully monomorphized, and equally efficient 23 | * Need to support `dyn AsyncRead` eventually (people will want to do that) 24 | * Need ability to 25 | 26 | ## Client: ship library in crates.io that leverages async traits 27 | 28 | Example from AWS Rust SDK: 29 | 30 | ```rust 31 | pub trait ProvideCredentials: Send + Sync + Debug { 32 | fn provide_credentials<'a>(&'a self) -> ProvideCredentials<'a>; 33 | } 34 | ``` 35 | 36 | People implement this like 37 | 38 | ```rust 39 | impl ProvideCredentials for MyType { 40 | fn provide_credentials<'a>(&'a self) -> ProvideCredentials<'a> { 41 | ProvideCredentials::new(async move { ... }) 42 | } 43 | } 44 | ``` 45 | 46 | where [`ProvideCredentials](https://docs.rs/aws-types/0.10.1/aws_types/credentials/future/struct.ProvideCredentials.html) is a struct that takes an `impl Future` and creates 47 | 48 | ## Embedded: ship library in crates.io that leverages async traits 49 | -------------------------------------------------------------------------------- /explainer/async_fn_in_dyn_trait/how_it_feels.md: -------------------------------------------------------------------------------- 1 | # How it feels to use 2 | 3 | ![planning rfc][] 4 | 5 | {{#include ../../badges.md}} 6 | 7 | Let's start with how we expect to *use* `dyn AsyncIterator`. This section will also elaborate some of our desiderata[^showoff], such as the ability to use `dyn AsyncIterator` conveniently in both std and no-std scenarios. 8 | 9 | [^showoff]: Ever since I once saw Dave Herman use this bizarre latin plural, I've been in love with it. --nikomatsakis 10 | 11 | ## How you write a function with a `dyn` argument 12 | 13 | We expect people to be able to write functions that take a `dyn AsyncIterator` trait as argument in the usual way: 14 | 15 | ```rust 16 | async fn count(i: &mut dyn AsyncIterator) -> usize { 17 | let mut count = 0; 18 | while let Some(_) = i.next().await { 19 | count += 1; 20 | } 21 | count 22 | } 23 | ``` 24 | 25 | One key part of this is that we want `count` to be invokable from both a std and a no-std environment. 26 | 27 | ## How you implement a trait with async fns 28 | 29 | This, too, looks like you would expect. 30 | 31 | ```rust 32 | struct YieldingRangeIterator { 33 | start: u32, 34 | stop: u32, 35 | } 36 | 37 | impl AsyncIterator for YieldingRangeIterator { 38 | type Item = u32; 39 | 40 | async fn next(&mut self) { 41 | if self.start < self.stop { 42 | let i = self.start; 43 | self.start += 1; 44 | tokio::thread::yield_now().await; 45 | Some(i) 46 | } else { 47 | None 48 | } 49 | } 50 | } 51 | ``` 52 | 53 | ## How you invoke `count` in std 54 | 55 | You invoke it as you normally would, by performing an unsize coercion. Invoking the method requires an allocator by default. 56 | 57 | ```rust 58 | let x = YieldingRangeIterator::new(...); 59 | let c = count(&mut x /* as &mut dyn AsyncIterator */).await; 60 | ``` 61 | -------------------------------------------------------------------------------- /evaluation.md: -------------------------------------------------------------------------------- 1 | # 🔬 Evaluation 2 | 3 | > The *evaluation* surveys the various design approaches that are under consideration. 4 | It is not required for all initiatives, only those that begin with a problem statement 5 | but without a clear picture of the best solution. Often the evaluation will refer to topics 6 | in the [design-discussions] for more detailed consideration. 7 | 8 | [design-discussions]: ./design-discussions/README.md 9 | 10 | ## Goals 11 | 12 | ### Write async fn in traits, impls 13 | 14 | The goal of the impact is to enable users to write `async fn` in traits and impls in a natural way. As a simple example, we would like to support the ability to write an `async fn` in any trait: 15 | 16 | ```rust 17 | trait Connection { 18 | async fn open(&mut self); 19 | async fn send(&mut self); 20 | async fn close(&mut self); 21 | } 22 | ``` 23 | 24 | Along with the corresponding impl: 25 | 26 | ```rust 27 | impl Connection for MyConnection { 28 | async fn open(&mut self) { 29 | ... 30 | } 31 | 32 | async fn send(&mut self) { 33 | ... 34 | } 35 | 36 | async fn close(&mut self) { 37 | ... 38 | } 39 | } 40 | ``` 41 | 42 | The goal in general is that `async fn` can be used in traits as widely as possible: 43 | 44 | * for foundational traits, like reading, writing, and iteration; 45 | * for async closures; 46 | * for async drop, which is built in to the language; 47 | * in `dyn` values, which introduce some particular complications; 48 | * in libraries, for all the usual reasons one uses traits; 49 | * in ordinary programs, using all manner of executors. 50 | 51 | ### Support async drop 52 | 53 | One particular trait worth discussing is the `Drop` trait. We would like to support "async drop", which means the ability to await things during drop: 54 | 55 | ```rust 56 | trait AsyncDrop { 57 | async fn drop(&mut self); 58 | } 59 | ``` 60 | 61 | Like `Drop`, the `AsyncDrop` trait would be -------------------------------------------------------------------------------- /.github/workflows/mdbook.yml: -------------------------------------------------------------------------------- 1 | # Sample workflow for building and deploying a mdBook site to GitHub Pages 2 | # 3 | # To get started with mdBook see: https://rust-lang.github.io/mdBook/index.html 4 | # 5 | name: Deploy mdBook site to Pages 6 | 7 | on: 8 | # Runs on pushes targeting the default branch 9 | push: 10 | branches: ["master"] 11 | 12 | # Allows you to run this workflow manually from the Actions tab 13 | workflow_dispatch: 14 | 15 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 16 | permissions: 17 | contents: read 18 | pages: write 19 | id-token: write 20 | 21 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 22 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 23 | concurrency: 24 | group: "pages" 25 | cancel-in-progress: false 26 | 27 | jobs: 28 | # Build job 29 | build: 30 | runs-on: ubuntu-latest 31 | steps: 32 | - uses: actions/checkout@v3 33 | - name: Install mdbook 34 | uses: baptiste0928/cargo-install@v2 35 | with: 36 | crate: mdbook 37 | version: "^0.4.21" 38 | args: --no-default-features 39 | - name: Install mdbook-yml-header 40 | uses: baptiste0928/cargo-install@v2 41 | with: 42 | crate: mdbook-yml-header 43 | version: "^0.1.2" 44 | - name: Setup Pages 45 | id: pages 46 | uses: actions/configure-pages@v3 47 | - name: Build with mdBook 48 | run: mdbook build 49 | - name: Upload artifact 50 | uses: actions/upload-pages-artifact@v1 51 | with: 52 | path: ./book 53 | 54 | # Deployment job 55 | deploy: 56 | environment: 57 | name: github-pages 58 | url: ${{ steps.deployment.outputs.page_url }} 59 | runs-on: ubuntu-latest 60 | needs: build 61 | steps: 62 | - name: Deploy to GitHub Pages 63 | id: deployment 64 | uses: actions/deploy-pages@v2 65 | -------------------------------------------------------------------------------- /explainer/inline_async_iter_adapter.md: -------------------------------------------------------------------------------- 1 | # Inline async iter adapter 2 | 3 | ![planning rfc][] 4 | 5 | {{#include ../badges.md}} 6 | 7 | This Appendix demonstrates how the inline async iterator adapter works. 8 | 9 | ```rust 10 | pub struct InlineAsyncIterator<'me, T: AsyncIterator> { 11 | iterator: T, 12 | future: MaybeUninit>, 13 | } 14 | 15 | impl AsyncIterator for InlineAsyncIterator 16 | where 17 | T: AsyncIterator, 18 | { 19 | type Item = T::Item; 20 | 21 | #[dyn(identity)] // default is "identity" 22 | fn next<'a>(&'a mut self) -> InlineFuture<'me, T::next<'me>> { 23 | let future: T::next<'a> = MaybeUninit::new(self.iterator.next()); 24 | // Why do we need this transmute? Is 'a not sufficient? 25 | let future: T::next<'me> = transmute(future); 26 | self.future = future; 27 | InlineFuture::new(self.future.assume_init_mut()) 28 | } 29 | } 30 | 31 | pub struct InlineFuture<'me, F> 32 | where 33 | F: Future 34 | { 35 | future: *mut F, 36 | phantom &'me mut F 37 | } 38 | 39 | impl<'me, F> InlineFuture<'me, F> { 40 | /// Unsafety condition: 41 | /// 42 | /// `future` must remain valid for all of `'me` 43 | pub unsafe fn new(future: *mut F) -> Self { 44 | Self { future } 45 | } 46 | } 47 | 48 | impl<'me, F> Future for InlineFuture<'me, F> 49 | where 50 | F: Future, 51 | { 52 | fn poll(self: Pin<&mut Self>, context: &mut Context) { 53 | // Justified by the condition on `new` 54 | unsafe { ... } 55 | } 56 | } 57 | 58 | impl IntoRawPointer for InlineFuture { 59 | type Target = F; 60 | 61 | // This return value is the part that has to be thin. 62 | fn into_raw(self) -> *mut Self::Target { 63 | self.future 64 | } 65 | 66 | // This will be the drop function in the vtable. 67 | unsafe fn drop_raw(this: *mut Self::Target) { 68 | unsafe { drop_in_place(self.future) } 69 | } 70 | } 71 | 72 | impl<'me> Drop for InlineFuture<'me, F> { 73 | fn drop(&mut self) { 74 | unsafe { ::drop_raw(self.future); } 75 | } 76 | } 77 | ``` 78 | -------------------------------------------------------------------------------- /explainer/async_fn_in_dyn_trait.md: -------------------------------------------------------------------------------- 1 | # Async fn in dyn trait 2 | 3 | ![planning rfc][] 4 | 5 | {{#include ../badges.md}} 6 | 7 | Welcome! This document explores how to combine `dyn` and `impl Trait` in return position. This is crucial pre-requisite for async functions in traits. As a motivating example, consider the trait `AsyncIterator`: 8 | 9 | ```rust 10 | trait AsyncIterator { 11 | type Item; 12 | 13 | async fn next(&mut self) -> Option; 14 | } 15 | ``` 16 | 17 | The `async fn` here is, of course, short for a function that returns `impl Future`: 18 | 19 | ```rust 20 | trait AsyncIterator { 21 | type Item; 22 | 23 | fn next(&mut self) -> impl Future>; 24 | } 25 | ``` 26 | 27 | The focus of this document is on how we can support `dyn AsyncIterator`. For an examination of why this is difficult, see [this blog post](https://smallcultfollowing.com/babysteps//blog/2021/09/30/dyn-async-traits-part-1/). 28 | 29 | ## Key details 30 | 31 | Here is a high-level summary of the key details of our approach: 32 | 33 | * Natural usage: 34 | * To use dynamic dispatch, just write `&mut dyn AsyncIterator`, same as any other trait. 35 | * Similarly, on the impl side, just write `impl AsyncIterator for MyType`, same as any other trait. 36 | * Allocation by default, but not required: 37 | * By default, trait functions that return `-> impl Trait` will allocate a `Box` to store the trait, but only when invoked through a `dyn Trait` (static dispatch is unchanged). 38 | * To support no-std or high performance scenarios, types can customize how an `-> impl Trait` function is dispatch through `dyn`. We show how to implement an `InlineAsyncIterator` type, for example, that wraps another `AsyncIterator` and stores the resulting futures on pre-allocated stack space. 39 | * rust-lang will publish a crate, `dyner`, that provides several common strategies. 40 | * Separation of concerns: 41 | * Users of a `dyn AsyncIterator` do not need to know (or care) whether the impl allocates a box or uses some other allocation strategy. 42 | * Similarly, authors of a type that implements `AsyncIterator` can just write an `impl`. That code can be used with any number of allocation adapters. 43 | 44 | -------------------------------------------------------------------------------- /evaluation/design/trait_multiplication.md: -------------------------------------------------------------------------------- 1 | # Trait multiplication 2 | 3 | ## Status 4 | 5 | Seems unlikely to be adopted, but may be the seed of a better idea 6 | 7 | ## Summary 8 | 9 | Introduce a new form of bound, trait *multiplication*. One can write `T: Iterator * Send` and it means `T: Iterator + Send` (using the notation from [RFC 2289]). More generally, `Foo * Bar` means `Foo + Bar` but also that `Foo::Assoc: Bar` for each associated type `Assoc` defined in `Foo` (including anonymous ones defined by async functions). 10 | 11 | [RFC 2289]: https://rust-lang.github.io/rfcs/2289-associated-type-bounds.html 12 | 13 | ## What's great about this 14 | 15 | The cool thing about this is that it means that **the check whether things are Send occurs exactly when it is needed, and not at the definition site**. This makes async functions in trait behave more analogously with ordinary impls. ([In contrast to implied bounds](./implied_send.html#not-analogous-to-async-fn-outside-of-traits).) 16 | 17 | With this proposal, the [background logging] scenario would play out differently depending on the [executor style] being used: 18 | 19 | [background logging]: ../scenarios/background-logging.md 20 | [executor style]: ../executor-styles.md 21 | 22 | * Thread-local: `logs: impl AsyncIterator + 'static` 23 | * Thread per core: `logs: impl AsyncIterator + Send + 'static` 24 | * Work stealing: `logs: impl AsyncIterator * Send + 'static` 25 | 26 | The key observation here is that `+ Send` only tells you whether the *initial value* (here, `logs`) is `Send`. The `* Send` is needed to say "and the futures resulting from this trait are Send", which is needed in work-stealing sceanrios. 27 | 28 | ## What's not so great about this 29 | 30 | ### Complex 31 | 32 | The distinction between `+` and `*` is subtle but crucial. It's going to be a new thing to learn and it just makes the trait system feel that much more complex overall. 33 | 34 | ### Reptitive for multiple traits 35 | 36 | If you had a number of async traits, you would need `* Send` for each one: 37 | 38 | ```rust 39 | trait Trait1 { 40 | async fn method1(&self); 41 | } 42 | 43 | trait Trait2 { 44 | async fn method2(&self); 45 | } 46 | 47 | async fn foo() 48 | where 49 | T: Send * (Trait1 + Trait2) 50 | { 51 | 52 | } 53 | ``` 54 | -------------------------------------------------------------------------------- /roadmap/impl_trait_in_traits.md: -------------------------------------------------------------------------------- 1 | # impl Trait in traits 2 | 3 | This effort is part of the [impl trait initiative]. Some notes are kept here as a summary. 4 | 5 | [impl trait initiative]: https://github.com/rust-lang/impl-trait-initiative 6 | 7 | ## Summary 8 | 9 | * Able to write `-> impl Trait` in traits 10 | * Able to write `type Foo<..> = impl Trait` in impls ([type alias impl trait], [generic associated types])) 11 | 12 | ## Requires 13 | 14 | * [Type alias impl trait] 15 | * [Generic associated types] 16 | 17 | [Type alias impl trait]: https://github.com/rust-lang/rust/issues/63063 18 | [Generic associated types]: https://github.com/rust-lang/generic-associated-types-initiative 19 | 20 | ## Design notes 21 | 22 | Support `-> impl Trait` (existential impl trait) in traits. Core idea is to desugar such thing into a (possibly generic) associated type: 23 | 24 | ```rust 25 | trait SomeTrait { 26 | fn foo<(&mut self) -> impl Future + '_; 27 | } 28 | 29 | // becomes something like: 30 | // 31 | // Editor's note: The name of the associated type is under debate; 32 | // it may or may not be something user can name, though they should 33 | // have *some* syntax for referring to it. 34 | 35 | trait SomeTrait { 36 | type Foo<'me>: Future + 'me 37 | where 38 | Self: 'me; 39 | 40 | async fn foo(&mut self) -> Self::Foo<'_>; 41 | } 42 | ``` 43 | 44 | We also need to support `-> impl Trait` in impls, in which case the body desugars to a "type alias impl trait": 45 | 46 | ```rust 47 | impl SomeTrait for SomeType { 48 | fn foo<(&mut self) -> impl Future + '_ { 49 | 50 | } 51 | } 52 | 53 | // becomes something using "type alias impl Trait", like this: 54 | 55 | trait SomeTrait { 56 | type Foo<'me> = impl Future + 'me 57 | where 58 | Self: 'me; 59 | 60 | fn foo(&mut self) -> Self::Foo<'_> { 61 | ... 62 | } 63 | } 64 | ``` 65 | 66 | ## Frequently asked questions 67 | 68 | ### What is the name of that GAT we introduce? 69 | 70 | - I called it `Bar` here, but that's somewhat arbitrary, perhaps we want to have some generic syntax for naming the method? 71 | - Or for getting the type of the method. 72 | - This problem applies equally to other "`-> impl Trait` in trait" scenarios. 73 | - [Exploration doc](https://hackmd.io/IISsYc0fTGSSm2MiMqby4A) 74 | 75 | -------------------------------------------------------------------------------- /roadmap/dyn_trait.md: -------------------------------------------------------------------------------- 1 | # Dyn trait 2 | 3 | ## Impact 4 | 5 | * Soundness holes relating to `dyn Trait` are closed. 6 | * The semver implication of whether a trait is "dyn or not" are clear. 7 | * More kinds of traits are dyn-safe. 8 | * Easily able to have a "dynamically dispatched core" with helper methods. 9 | * Users are able to the "adaptation" from a statically known type (`T: Trait`) into a `dyn Trait`. 10 | 11 | ## Design notes 12 | 13 | ### Soundness holes 14 | 15 | FIXME-- list various issues here :) 16 | 17 | ### Semver implications 18 | 19 | Today, the compiler automatically determines whether a trait is "dyn-safe". This means that otherwise legal additions to the trait (such as new ) 20 | 21 | ### More kinds of traits are dyn-safe 22 | 23 | Currently dyn-safe traits exclude a lot of functionality, such as generic methods. We may be able to lift some of those restrictions. 24 | 25 | ### Easily able to have a "dynamically dispatched core" with helper methods 26 | 27 | There is a common pattern with e.g. `Iterator` where there is a dynamically dispatched "core method" (`fn next()`) and then a variety of combinators and helper methods that use `where Self: Sized` to side-step dyn-safety checks. These methods often involve generics. We should make this pattern easier and more obvious, and (ideally) make it work better -- e.g., by having those methods *also* available on `dyn Trait` receivers (which seems fundamentally possible). 28 | 29 | ### Adaptation 30 | 31 | In the case of async Rust, given a trait `Foo` that contains `async fn` methods, we wish to be able to have the user write `dyn Foo` without having to specify the values of the associated types that contain the future types for those methods. Consider the fully desugard example: 32 | 33 | ```rust 34 | trait Foo { 35 | type Method<..>: Future; 36 | fn method() -> Self::Method<..> 37 | } 38 | ``` 39 | 40 | Roughly speaking we wish to be able to supply an impl like 41 | 42 | ```rust 43 | impl Foo for dyn Foo { 44 | type Method<..> = Box>; 45 | fn method() -> Self::Method { 46 | // call, via vtable, a shim that will create the `Box` 47 | // (or whichever smart pointer is desired) 48 | } 49 | } 50 | ``` 51 | 52 | Ideally, this would be a general capability that users can use to control the adaptation of "known types" to `dyn` types for other traits. 53 | -------------------------------------------------------------------------------- /evaluation/challenges/dyn_traits.md: -------------------------------------------------------------------------------- 1 | # Dyn traits 2 | 3 | Supporting `dyn Trait` when `Trait` contains an `async fn` is challenging: 4 | 5 | ```rust 6 | trait Trait { 7 | async fn foo(&self); 8 | } 9 | 10 | impl Trait for TypeA { 11 | async fn foo(&self); 12 | } 13 | 14 | impl Trait for TypeB { ... } 15 | ``` 16 | 17 | Consider the desugared form of this trait: 18 | 19 | ```rust 20 | trait Trait { 21 | type Foo<'s>: Future + 's; 22 | 23 | fn foo(&self) -> Self::Foo<'_>; 24 | } 25 | 26 | impl Trait for TypeA { 27 | type Foo<'s> = impl Future + 's; 28 | 29 | fn foo(&self) -> Self::Foo<'_> { 30 | async move { ... } // has some unique future type F_A 31 | } 32 | } 33 | 34 | impl Trait for TypeB { ... } 35 | ``` 36 | 37 | The primary challenge to using `dyn Trait` in today's Rust is that **`dyn Trait` today must list the values of all associated types**. This means you would have to write `dyn for<'s> Trait = XXX>` where `XXX` is the future type defined by the impl, such as `F_A`. This is not only verbose (or impossible), it also uniquely ties the `dyn Trait` to a particular impl, defeating the whole point of `dyn Trait`. 38 | 39 | For this reason, the `async_trait` crate models all futures as `Box>`: 40 | 41 | ```rust 42 | #[async_trait] 43 | trait Trait { 44 | async fn foo(&self); 45 | } 46 | 47 | // desugars to 48 | 49 | trait Trait { 50 | fn foo(&self) -> Box + Send + '_>; 51 | } 52 | ``` 53 | 54 | This compiles, but it has downsides: 55 | 56 | * Allocation is required, *even when not using dyn Trait*. 57 | * The user must state up front whether `Box` is `Send` or not. 58 | * In `async_trait`, this is declared by writing `#[async_future(?Send)]` if desired. 59 | 60 | ## Desiderata 61 | 62 | Here are some of the general constraints: 63 | 64 | * The ability to use `async fn` in a trait without allocation 65 | * When using a `dyn Trait`, the type of the future must be the same for all impls 66 | * This implies a `Box` or other pointer indirection, or something like [inline async fn](../design/inline_async_fn.md). 67 | * It would be nice if it were possible to use `dyn Trait` in an embedded context (without access to `Box`) 68 | * This will not be possible "in general", but it could be possible for particular traits, such as `AsyncIterator` -------------------------------------------------------------------------------- /evaluation/scenarios/background-logging.md: -------------------------------------------------------------------------------- 1 | # Background logging 2 | 3 | In this scenario, the `start_writing_logs` function takes an async iterable and spawns out a new task. This task will pull items from the iterator and send them to some server: 4 | 5 | ```rust 6 | trait AsyncIterator { 7 | type Item; 8 | async fn next(&mut self) -> Self::Item; 9 | } 10 | 11 | // Starts a task that will write out the logs in the background 12 | async fn start_writing_logs( 13 | logs: impl AsyncIterator + 'static 14 | ) { 15 | spawn(async move || { 16 | while let Some(log) = logs.next().await { 17 | send_to_serve(log).await; 18 | } 19 | }); 20 | } 21 | ``` 22 | 23 | The precise signature and requirements for the `spawn` function here will depend on what [kind of executor](../executor-styles.md) you are using, so let's consider each case but let's consider each case separately. 24 | 25 | One note: in [tokio] and other existing executors, the `spawn` function takes a future, not an async closure. We are using a closure here because that is more analogous to the synchronous signature, but also because it enables a distinction between the *initial state* and the future that runs. 26 | 27 | ## Thread-local executor 28 | 29 | This is the easy case. Nothing has to be `Send`. 30 | 31 | ## Work-stealing executor 32 | 33 | In this case, the spawn function will require both that the initial closure itself is `Send` and that the future it returns is `Send` (so that it can be moved from place to place as code executes). 34 | 35 | We don't have a good way to express this today! The problem is that there is a future that results from calling `logs.next()`, let's call it `F`. The future to be *spawned* has to be sure that `F: Send`. There isn't a good way to do this today, and even explaining the problem is surprisingly hard. Here is a "desugared version" of the program that shows what is needed: 36 | 37 | ```rust 38 | trait AsyncIterator { 39 | type Item; 40 | type NextFuture: Future; 41 | 42 | fn next(&mut self) -> impl Self::NextFuture; 43 | } 44 | 45 | // Starts a task that will write out the logs in the background 46 | async fn start_writing_logs( 47 | logs: I 48 | ) 49 | where 50 | I: AsyncIterator + 'static + Send, 51 | I::NextFuture: Send, 52 | { 53 | spawn(async move || { 54 | while let Some(log) = logs.next().await { 55 | send_to_serve(log).await; 56 | } 57 | }); 58 | } 59 | ``` 60 | 61 | (With [RFC 2289], you could write `logs: impl AsyncIterator + Send`, which is more compact, but still awkward.) 62 | 63 | [RFC 2289]: https://rust-lang.github.io/rfcs/2289-associated-type-bounds.html -------------------------------------------------------------------------------- /roadmap/async_closures.md: -------------------------------------------------------------------------------- 1 | # Async closures 2 | 3 | ## Impact 4 | 5 | * Able to create async closures that work like ordinary closures but which can await values. 6 | * Analogous traits to `Fn`, `FnMut`, `FnOnce`, etc 7 | * Reconcile async blocks and async closures 8 | 9 | ## Design notes 10 | 11 | The fundamental problem async closures are meant to solve is that **normal closures can't return a value that borrows from the closure itself** (or, by extension, anything it captures). This is a big problem in async because the execution of all async code is encapsulated in a future returned by our function. Since that asynchronous code often needs to operate on captured values, it must in turn borrow from the closure. 12 | 13 | This blog post describes the problem in more detail: . 14 | 15 | One of the assertions made by this post is that async functions need their own traits, analogous to `Fn` and friends: 16 | 17 | ```rust 18 | trait AsyncFnMut { 19 | type Output; 20 | async fn call(&mut self, args: A) -> Self::Output; 21 | } 22 | 23 | // Similarly for AsyncFn, AsyncFnOnce. 24 | ``` 25 | 26 | A generalization of this would be to define "lending function" traits. 27 | 28 | ```rust 29 | trait LendingFnMut { 30 | type Output<'this> 31 | where 32 | Self: 'this; 33 | 34 | fn call(&mut self, args: A) -> Self::Output<'_>; 35 | // ^ ^^^^ 36 | // Lends data from `self` as part of return value. 37 | } 38 | ``` 39 | 40 | This trait is similar to what `AsyncFnMut` above desugars to, except without saying anything about async or futures. 41 | 42 | The `LendingFnMut` trait is from a follow up post that explains how we can actually extend the *existing* `Fn` traits to support "lending functions" that can do exactly what we want. It takes advantage of the fact that existing Fn trait bounds must use a special `Fn(A, B) -> C` syntax that always specifies their output type. 43 | 44 | 45 | 46 | Other notes: 47 | 48 | `AsyncFnOnce` is almost the same as `Future`/`Async` -- both represent, effectively, a future that can be driven exactly once. The difference is that your type can distinguish statically between the uncalled state and the persistent state after being called, if you wish, by using separate types for each. This can be useful for situations where an `async fn` is `Send` up until the point it is called, at which point it creates inner state that is not `Send`. 49 | 50 | The concept of `AsyncFn` is more clear, but it requires storing the state externally to make sense: how else can there be multiple parallel executions. 51 | -------------------------------------------------------------------------------- /explainer/async_fn_in_dyn_trait/unresolved.md: -------------------------------------------------------------------------------- 1 | # Unresolved questions 2 | 3 | ![planning rfc][] 4 | 5 | {{#include ../../badges.md}} 6 | 7 | There are some interesting details that are yet to be resolved, and they become important indeed in the "feel" of the overall design. Covering those details however would make this document too long, so we're going to split it into parts. Nonetheless, for completeness, this section lists out some of those "questions yet to come". 8 | 9 | ## Which values can be converted into a dynx struct? 10 | 11 | In the previous section, we showed how a `#[dyn(identity)]` function must return "something that can be converted into a dynx struct", and we showed that a case of returning a `Pin>` type. But what are the general rules for constructing a `dynx` struct? You're asking the right question, but that's a part of the design we haven't bottomed out yet. See [this design document](../../evaluation/design/dynx/creation.md) for more details. 12 | 13 | ## What about dyn with sendable future, how does that work? 14 | 15 | We plan to address this in a follow-up RFC. The core idea is to build on the notation that one would use to express that you wish to have an async fn return a `Send`. As an example, one might write `AsyncIterator` to indicate that `next()` returns a `Send` future; when we generate the vtable for a `dyn AsyncIterator`, we can ensure that the bounds for `next` are applied to its return type, so that it would return a `dynx Future + Send` (and not just a `dynx Future`). We have also been exploring a more convenient shorthand for declaring "all the futures returned by methods in trait should be `Send`", but to avoid bikeshedding we'll avoid talking more about that in this document! See [this design document](../../evaluation/design/dynx/auto_trait.md) for more details. 16 | 17 | ## How do `dynx Trait` structs and "sealed traits" interact? 18 | 19 | As described here, every dyn-safe trait `Trait` gets an "accompanying" `dynx Trait` struct and an `impl Trait for dynx Trait` impl for that struct. This can have some surprising interactions with unsafe code -- if you have a trait that can only be safely implemented by types that meet certain criteria, the impl for a `dynx` type may not meet those criteria. This can lead to undefined behavior. The question then is: *whose fault is that?* In other words, is it the language's fault, for adding impls you didn't expect, or the code author's fault, for not realizing those impls would be there (or perhaps for not declaring that their trait had additional safety requirements, e.g. by making the trait unsafe). This question turns out to be fairly complex, so we'll defer a detailed discussion beyond this summary. See [this design document](../../evaluation/design/dynx/sealed_traits.md) for more details. 20 | -------------------------------------------------------------------------------- /CHARTER.md: -------------------------------------------------------------------------------- 1 | # 📜 async fn fundamentals Charter 2 | 6 | 7 | This initiative is part of the wg-async-foundations [vision process]. 8 | 9 | ## Proposal 10 | 11 | `async fn` exists today, but does not integrate well with many core language features like traits, closures, and destructors. We would like to make it so that you can write async code just like any other Rust code. 12 | 13 | ## Goals 14 | 15 | ### Able to write `async fn` in traits and trait impls 16 | 17 | The goal in general is that `async fn` can be used in traits as widely as possible: 18 | 19 | * for foundational traits, like reading, writing, and iteration; 20 | * for async closures; 21 | * for async drop, which is built in to the language; 22 | * in `dyn` values, which introduce some particular complications; 23 | * in libraries, for all the usual reasons one uses traits; 24 | * in ordinary programs, using all manner of executors. 25 | 26 | #### Key outcomes 27 | 28 | * [Async functions in traits][async trait] desugar to [impl Trait in traits] 29 | * Traits that use `async fn` must still be [dyn safe][dyn async trait] though some tuning may be required 30 | * Return futures must easily be [bound by `Send`][bounding] 31 | 32 | ### Support async drop 33 | 34 | Users should be able to write ["async fn drop"][async drop] to declare that the destructor may await. 35 | 36 | #### Key outcomes 37 | 38 | * Types can perform async operations on cleanup, like closing database connections 39 | * There's a way to detect and handle async drop types that are dropped synchronously 40 | * Await points that result from async cleanup can be identified, if needed 41 | 42 | ### Support async closures 43 | 44 | Support [async closures] and `AsyncFn`, `AsyncFnMut`, `AsyncFnOnce` traits. 45 | 46 | #### Key outcomes 47 | 48 | * Async closures work like ordinary closures but can await values 49 | * Traits analogous to `Fn`, `FnMut`, `FnOnce` exist for async 50 | * Reconcile async blocks and async closures 51 | 52 | ## Membership 53 | 54 | | Role | Github | 55 | | --- | --- | 56 | | [Owner] | [tmandry](https://github.com/tmandry) | 57 | | [Liaison] | [nikomatsakis](https://github.com/nikomatsakis) | 58 | 59 | [Owner]: https://lang-team.rust-lang.org/initiatives/process/roles/owner.html 60 | [Liaison]: https://lang-team.rust-lang.org/initiatives/process/roles/liaison.html 61 | 62 | [async drop]: ./design-discussions/async_drop.md 63 | [async closures]: ./design-discussions/async_closures.md 64 | [async trait]: ./design-discussions/static_async_trait.md 65 | [bounding]: ./evaluation/challenges/bounding_futures.html 66 | [dyn async trait]: ./design-discussions/dyn_async_trait.md 67 | [impl Trait in traits]: ./design-discussions/impl_trait_in_traits.md 68 | [vision process]: https://rust-lang.github.io/wg-async-foundations/vision.html 69 | -------------------------------------------------------------------------------- /explainer/async_fn_in_dyn_trait/how_it_works.md: -------------------------------------------------------------------------------- 1 | # How it works 2 | 3 | ![planning rfc][] 4 | 5 | {{#include ../../badges.md}} 6 | 7 | This section is going to dive into the details of how this proposal works within the compiler. As a user, you should not generally have to know these details unless you intend to implement your own adapter shims. 8 | 9 | ## Return to our running example 10 | 11 | Let's repeat our running example trait, `AsyncIterator`: 12 | 13 | ```rust 14 | trait AsyncIterator { 15 | type Item; 16 | 17 | async fn next(&mut self) -> Option; 18 | } 19 | 20 | async fn count(i: &mut dyn AsyncIterator) -> usize { 21 | let mut count = 0; 22 | while let Some(_) = i.next().await { 23 | count += 1; 24 | } 25 | count 26 | } 27 | ``` 28 | 29 | Recall that `async fn next` desugars to a regular function that returns an `impl Trait`: 30 | 31 | ```rust 32 | trait AsyncIterator { 33 | type Item; 34 | 35 | fn next(&mut self) -> impl Future>; 36 | } 37 | ``` 38 | 39 | Which in turn desugars to a trait with an associated type (note: precise details here are still being debated, but this desugaring is "close enough" for our purposes here): 40 | 41 | ```rust 42 | trait AsyncIterator { 43 | type Item; 44 | 45 | type next<'me>: Future> 46 | fn next(&mut self) -> Self::next<'_>; 47 | } 48 | ``` 49 | 50 | ## Challenges of invoking an async fn through `dyn` 51 | 52 | When `count` is compiled, it needs to invoke `i.next()`. But the `AsyncIterator` trait simply declares that `next` returns some `impl Future` (i.e., "some kind of future"). Each impl will define its own return type, which will be some kind of special struct that is "sufficiently large" to store all the state that needed by that particular impl. 53 | 54 | When an async function is invoked through static dispatch, the caller knows the exact type of iterator it has, and hence knows exactly how much stack space is needed to store the resulting future. When `next` is invoked through a `dyn AsyncIterator`, however, we can't know the specific impl and hence can't use that strategy. Instead, what happens is that invoking `next` on a `dyn AsyncIterator` *always* yields a *pointer* to a future. Pointers are always the same size no matter how much memory they refer to, so this strategy doesn't require knowing the "underlying type" beneath the `dyn AsyncIterator`. 55 | 56 | Returning a pointer, of course, requires having some memory to point at, and that's where things get tricky. The easiest (and default) solution is to allocate and return a `Pin>`, but we wish to support other kinds of pointers as well (e.g., pointers into pre-allocated stack space). We'll explain how our system works in stages, first exploring a version that hardcodes the use of `Box`, and then showing how that can be generalized. 57 | -------------------------------------------------------------------------------- /evaluation/design/bound_items.md: -------------------------------------------------------------------------------- 1 | # Bound Items 2 | 3 | ## Summary 4 | 5 | Targets the [bounding futures](../challenges/bounding-futures.md) challenge. 6 | 7 | This is a series of smaller changes (see detailed design), with the goal of allowing the end user to name the bound described in the challenge text in this way (i'll let the code speak for itself): 8 | ```rust 9 | pub trait AsyncIterator { 10 | type Item; 11 | async fn next(&mut self) -> Option; 12 | } 13 | 14 | pub async fn start_writing_logs( 15 | logs: F 16 | ) where IsThreadSafeIterator, F: 'static { // IsThreadSafeIterator is defined below 17 | todo!() 18 | } 19 | 20 | pub bound IsThreadSafeAsyncIterator { 21 | T: AsyncIterator + Send, 22 | // Add a `fn` keyword here to refer to the type of associated function. 23 | ::fn next: SimpleFnOnceWithSendOutput, 24 | // Use `*` to refer to potentially long list of all associated functions. 25 | // this is useful in certain cases. 26 | ::fn *: SimpleFnOnceWithSendOutput, 27 | } 28 | 29 | ``` 30 | 31 | ## Detailed design 32 | 33 | I'm not good at naming things, and all names and identifiers are subject to bikeshedding. 34 | 35 | * Bound Item(Language construct). Allow user to name a combination of bounds. No `Self` allowed here. 36 | Could replace the `trait_alias` language feature. Improve ergonomics of many existing code if used 37 | properly. 38 | 39 | I'm hesitant on which of `PascalCase`, `snake_case`, or `UPPER_CASE` should be used for this naming though. 40 | 41 | * Syntax for refering to the type of a associated item in path. Currently using `fn` keyword. Can expand to 42 | `const` and `type` keywords if needed. Turbofish might be involved if the assoc item is generic. 43 | 44 | * `SimpleFnOnce` trait (Language item). A function can only accept one set of arguments, 45 | so all functions and closures shall implement this, while user-defined callable types doesn't 46 | have to. This is used for reasoning about the output parameters of associated functions. 47 | ```rust 48 | pub trait SimpleFnOnce: FnOnce { 49 | type Arg; 50 | } 51 | ``` 52 | * `SimpleFnOnceWithSendOutput` trait (Lib construct or user-defined). 53 | ```rust 54 | pub trait SimpleFnOnceWithSendOutput : SimpleFnOnce where 55 | Self::Output: Send 56 | {} 57 | 58 | impl SimpleFnOnceWithSendOutput for T where T:SimpleFnOnce, T::Output: Send {} 59 | ``` 60 | 61 | ## What's great about this 62 | * Absolutely minimal type system changes. 63 | * Quite easy to learn. (Name it and use the name) 64 | * Easy to write library documentation, and give examples. 65 | 66 | ## What's not so great 67 | * New syntax item constructs. Three parsers (rustc, ra, syn) needs to change to support this. 68 | * Very verbose code writing style. Many more lines of code. 69 | * Will become part of library API in many cases. 70 | -------------------------------------------------------------------------------- /explainer/async_fn_in_dyn_trait/identity_shim_functions.md: -------------------------------------------------------------------------------- 1 | # Identity shim functions: avoiding the box allocation 2 | 3 | ![planning rfc][] 4 | 5 | {{#include ../../badges.md}} 6 | 7 | 8 | In the previous section, we explained how the default "shim" created for an `async fn` allocates `Box` to store the future; this `Box` is then converted to a `dynx Future` when it is returned. Using `Box` is a convenient default, but of course it's not always the right choice: for this reason, you can customize what kind of shim using an attribute, `#[dyn]`, attached to the method in the impl: 9 | 10 | * `#[dyn(box)]` -- requests the default strategy, allocating a box 11 | * `#[dyn(identity)]` -- requests a shim that just converts the returned future into a `dynx`. The returned future must be of a suitable pointer type (more on that in the next section). 12 | 13 | An impl of `AsyncIterator` that uses the default boxing strategy *explicitly* would look like this: 14 | 15 | ```rust 16 | impl AsyncIterator for YieldingRangeIterator { 17 | type Item = u32; 18 | 19 | #[dyn(box)] 20 | async fn next(&mut self) { /* same as above */ } 21 | } 22 | ``` 23 | 24 | If we want to avoid the box, we can instead write an impl for `AsyncIterator` that uses `dyn(identity)`. In this case, the impl is responsible for converting the `impl Future` return value into a an appropriate pointer from which a `dynx` can be constructed. For example, suppose that we are ok with allocating a `Box`, but we want to do it from a custom allocator. What we would like is an adapter `InAllocator` which adapts some `I: AsyncIterator` so that its futures are boxed in a particular allocator. You would use it like this: 25 | 26 | ```rust 27 | fn example(allocator: A) { 28 | let mut iter = InAllocator::new(allocator, YieldingRangeIterator::new(); 29 | fn_that_takes_dyn(&mut iter); 30 | } 31 | 32 | fn fn_that_takes_dyn(x: &mut dyn AsyncIterator) { 33 | // This call will go into the `InAllocator` and 34 | // hence will allocate a box using the custom allocator `A`: 35 | let value = x.next().await; 36 | } 37 | ``` 38 | 39 | To implement `InAllocator`, we first define the struct itself: 40 | 41 | ```rust 42 | struct InAllocator { 43 | allocator: A, 44 | iterator: I, 45 | } 46 | 47 | impl InAllocator { 48 | pub fn new( 49 | allocator: A, 50 | iterator: I, 51 | ) -> Self { 52 | Self { allocator, iterator } 53 | } 54 | } 55 | ``` 56 | 57 | and then we implement `AsyncIterator` for `InAllocator<..>`, annotating the `next` method with `#[dyn(identity)]`. 58 | The `next` method 59 | 60 | ```rust 61 | impl AsyncIterator for InAllocator 62 | where 63 | A: Allocator + Clone, 64 | I: AsyncIterator, 65 | { 66 | type Item = u32; 67 | 68 | #[dyn(identity)] 69 | fn next(&mut self) -> Pin> { 70 | let future = self.iterator.next(); 71 | Pin::from(Box::new_in(future, self.allocator.clone())) 72 | } 73 | } 74 | ``` 75 | -------------------------------------------------------------------------------- /updates/2021-oct.md: -------------------------------------------------------------------------------- 1 | # 2021-Oct: Lang team update 2 | 3 | * Owner: tmandry 4 | * Liaison/author: nikomatsakis, currently (looking for a replacement) 5 | 6 | Although the async fundamentals initiative hasn't technically formed yet, I'm going to write an update anyhow as "acting liaison". To start, I would like to find another liaison! I think that I am a bit close to the work here and the group would benefit from a liaison who is a bit more distant. 7 | 8 | Our overall [charter]: Make it possible to write `async fn` in traits, as well as enabling key language features that bring async more into parity with sync: 9 | 10 | * Async functions in traits 11 | * in both static and `dyn` contexts 12 | * Async drop 13 | * Async closures 14 | 15 | This is a key enabler for most of the async vision doc. For example, the various interop traits (e.g., async iteration, async read, async write, etc) all build on async functions in traits. 16 | 17 | We have identified an [MVP], which aims to support async fn in traits in static contexts by desugaring to an (anonymous) associated GAT plus (on the impl side) a TAIT. We are preparing an RFC describing this MVP and talking to various folks about doing the implementation work. 18 | 19 | We are assembling a group of [stakeholders](https://hackmd.io/y31gA3ElSu2DUdY6vUGs8A) that we will talk to in order to get feedback on the MVP and on future design decisions (in addition to the lang team and so forth). 20 | 21 | In addition to the MVP, we are drafting an [evaluation doc] that identifies further challenges along with possible solutions. Once we feel good about the coverage for a particular challenge, we will create targeted RFCs for that specific item. 22 | 23 | [mvp]: https://rust-lang.github.io/async-fundamentals-initiative/roadmap/mvp.html 24 | [charter]: https://rust-lang.github.io/async-fundamentals-initiative/CHARTER.html 25 | [evaluation doc]: https://rust-lang.github.io/async-fundamentals-initiative/evaluation.html 26 | 27 | One specific direction of interest is creating *core enablers* that can be used to experiment with the most ergonomic syntax or capabilities. As an example, for dyn async traits, there is a need to return some form of "boxed dyn future", but there are many runtiem techniques one might use for this (the most obvious being to return a `Box`, of course). Supporting those options requires being able to manipulate vtables and the like. It may be an optional to make those kind of "core capabilities" available as simple primitives. This would allow us to experiment with a procedural macro that generates an easy to use wrapper built on these primitives; once we have a clear idea what exactly that should be, we can bring it into the language. (It remains to be seen if this is a better path than trying to build the say thing first and work out the primitives later.) 28 | 29 | Niko has also been writing blog posts to walk through the dyn logic in more detail (starting at [part 1](https://smallcultfollowing.com/babysteps//blog/2021/09/30/dyn-async-traits-part-1/) and continuing in [part 2](https://smallcultfollowing.com/babysteps//blog/2021/10/01/dyn-async-traits-part-2/)). 30 | -------------------------------------------------------------------------------- /roadmap/dyn_async_trait.md: -------------------------------------------------------------------------------- 1 | # Dyn async trait 2 | 3 | ## Impact 4 | 5 | * Traits that contain `async fn` or [impl trait in traits](./impl_trait_in_traits.md) can still be dyn safe 6 | * Costs like boxing of futures are limited to code that uses `dyn Trait` and not to all users of the trait 7 | * Reasonable defaults around things like `Send + Sync` and what kind of boxing is used 8 | * Ability to customize those defaults for individual traits or on a crate-wide or module-wide basis 9 | 10 | ## Requires 11 | 12 | * [dyn trait](./dyn_trait.md) 13 | 14 | ## Design notes 15 | 16 | * Permit a trait `TheTrait` containing `async fn` or [impl trait in traits](./impl_trait_in_traits.md) to be used with `dyn TheTrait`, at least if other criteria are met. 17 | * Do not require annoying annotations. 18 | * Permit the user to select, for `TheTrait`, how the futures will be boxed or otherwise represented, which would permit us to use `Box` or potentially other types like `SmallBox` etc. 19 | * User should also be able to control whether the resulting futures are assumed to be send. 20 | 21 | ### Older notes 22 | 23 | The most basic desugaring of async fn in traits will make the trait not dyn-safe. "Inline" async fn in traits is one way to circumvent that, but it's not suitable for all traits that must be dyn-safe. There are other efficient options: 24 | 25 | - Return a `Box>` -- but then we must decide if it will be `Send`, right? And we'd like to only do that when using the trait as a `dyn Trait`. Plus it is not compatible with no-std (it is compatible with alloc). 26 | - This comes down to needing some form of opt-in. 27 | 28 | This concern applies equally to other "`-> impl Trait` in trait" scenarios. 29 | 30 | We have looked at revising how "dyn traits" are handled more generally in the lang team on a number of occasions, but [this meeting](https://github.com/rust-lang/lang-team/blob/master/design-meeting-minutes/2020-01-13-dyn-trait-and-coherence.md) seems particularly relevant. In that meeting we were discussing some soundness challenges with the existing dyn trait setup and discussing how some of the directions we might go enabled folks to write their _own_ `impl Trait for dyn Trait` impls, thus defining for themselves how the mapping from Trait to dyn Trait. This seems like a key piece of the solution. 31 | 32 | One viable route might be: 33 | 34 | - Traits using `async fn` are not, by default, dyn safe. 35 | - You can declare how you want it to be dyn safe: 36 | - `#[repr(inline)]` 37 | - or `#[derive(dyn_async_boxed)]` or some such 38 | - to take an `#[async_trait]`-style approach 39 | - It would be nice if users can declare their own styles. For example, Matthias247 pointed out that the `Box` used to allocate can be reused in between calls for increased efficiency. 40 | - It would also be nice if there's an easy, decent default -- maybe you don't even _have_ to opt-in to it if you are not in `no_std` land. 41 | 42 | ## Frequently asked questions 43 | 44 | ### What are the limitations around allocation and no-std code? 45 | 46 | "It's complicated". A lot of no-std code does have an allocator (it depends on alloc), though it may require fallible allocation, or permit allocation of fixed quantities (e.g., only at startup, or so long as it can be known to be O(1)). 47 | -------------------------------------------------------------------------------- /explainer/async_fn_in_dyn_trait/hardcoding_box.md: -------------------------------------------------------------------------------- 1 | # How the running example could work with `Box` 2 | 3 | ![planning rfc][] 4 | 5 | {{#include ../../badges.md}} 6 | 7 | Before we get into the full system, we're going to start by *just* explaining how a system that hardcodes `Pin>` would work. In that case, if we had a `dyn AsyncIterator`, the vtable for that async-iterator would be a struct sort of like this: 8 | 9 | ```rust 10 | struct AsyncIteratorVtable { 11 | type_tags: usize, 12 | drop_in_place_fn: fn(*mut ()), // function that frees the memory for this trait 13 | next_fn: fn(&mut ()) -> Pin + '_>> 14 | } 15 | ``` 16 | 17 | This struct has three fields: 18 | 19 | * `type_tags`, which stores type information used for [`Any`](https://doc.rust-lang.org/std/any/trait.Any.html) 20 | * `drop_in_place_fn`, a funcdtion that drops the memory of the underlying value. This is used when the a `dyn AsyncIterator` is dropped; e.g., when a `Box` is dropped, it calls [`drop_in_place`](https://doc.rust-lang.org/std/ptr/fn.drop_in_place.html) on its contents. 21 | * `next_fn`, which stores the function to call when the user invokes `next`. You can see that this function is declared to return a `Pin>`. 22 | 23 | (This struct is just for explanatory purposes; if you'd like to read more details about vtable layout, see [this description](https://rust-lang.github.io/dyn-upcasting-coercion-initiative/design-discussions/vtable-layout.html).) 24 | 25 | Invoking `i.next()` (where `i: &mut dyn AsynIterator`) ultimately invokes the `next_fn` from the vtable and hence gets back a `Pin>`: 26 | 27 | ```rust 28 | i.next().await 29 | 30 | // becomes 31 | 32 | let f: Pin>>> = i.next(); 33 | f.await 34 | ``` 35 | 36 | ## How to build a vtable that returns a boxed future 37 | 38 | We've seen how `count` calls a method on a `dyn AsyncIterator` by loading `next_fn` from the vtable, but how do we construct that vtable in the first place? Let's consider the struct `YieldingRangeIterator` and its `impl` of `AsyncIterator` that we saw before in an earlier section: 39 | 40 | 41 | ```rust 42 | struct YieldingRangeIterator { 43 | start: u32, 44 | stop: u32, 45 | } 46 | 47 | impl AsyncIterator for YieldingRangeIterator { 48 | type Item = u32; 49 | 50 | async fn next(&mut self) {...} 51 | } 52 | ``` 53 | 54 | There's a bit of a trick here. Normally, when we build the vtable for a trait, it points directly at the functions from the impl. But in this case, the function in the impl has a different return type: instead of returning a `Pin>`, it returns some `impl Future` type that could have any size. This is a problem. 55 | 56 | To solve it, the vtable doesn't directly reference the `next` fn from the impl, instead it references a "shim" function that allocates the box: 57 | 58 | ```rust 59 | fn yielding_range_shim( 60 | this: &mut YieldingRangeIterator, 61 | ) -> Pin>>> { 62 | Box::pin(::next(this)) 63 | } 64 | ``` 65 | 66 | This shim serves as an "adaptive layer" on the callee's side, converting from the `impl Future` type to the `Box`. More generally, we can consider the process of invoking a method through a dyn as having adaptation on both sides, like shown in this diagram: 67 | 68 | ![diagram](https://gist.githubusercontent.com/nikomatsakis/c0772e1827fd50e72c5052c8504b8a69/raw/4e943c7f762ec9ded11b5467ae952a70a5c4c24a/diagram.svg) 69 | 70 | (This diagram shows adaptation happening to the arguments too; but for this part of the design, we only need the adaptation on the return value.) 71 | -------------------------------------------------------------------------------- /stakeholders.md: -------------------------------------------------------------------------------- 1 | # Stakeholders 2 | 3 | ## Stakeholder representatives 4 | 5 | ### The pitch 6 | 7 | The [async fundamentals initiative](https://rust-lang.github.io/async-fundamentals-initiative/) is developing designs to bring async Rust "on par" with synchronous Rust in terms of its core capabilities: 8 | 9 | * async functions in traits (our initial focus) 10 | * async drop (coming later) 11 | * async closures (coming later) 12 | 13 | We need feedback from people using Rust in production to ensure that our designs will meet their needs, but also to help us get some sense of how easy they will be to understand. One of the challenges with async Rust is that it has a lot of possible variations, and getting a sense for what kinds of capabilities are most important will help us to bias the designs. 14 | 15 | **We also want people to commit to experimenting with these designs while they are on nightly!** This doesn't mean that you have to ship production software based on the nightly compiler. But it does mean that you agree to, perhaps, port your code over to use the nightly compiler on a branch and tell us how it goes. Or experiment with the nightly compiler on other codebases you are working on. 16 | 17 | ### Expected time commitment 18 | 19 | * One 90 minute meeting per month + written feedback 20 | * Stucture: 21 | * ~30 minute presentation covering the latest thoughts 22 | * ~60 minutes open discussion with Tyler, Niko, other stakeholders 23 | * Written feedback: 24 | * answer some simple questions, provide overall perspective 25 | * expected time: ~30 minutes or less 26 | * Once features become available (likely early next year), creating branch that uses them 27 | * We will do our best to make this easy 28 | * For example, I expect us to offer an alternative to `async-trait` procedural macro that generates code that requires the nightly compiler 29 | * But this will still take some time! How much depends a bit on you. 30 | * Let's guess-timate 2-3 hours per month 31 | 32 | ### Benefits 33 | 34 | * Shape the design of async fn in traits 35 | * Help ensure that it works for you 36 | * A t-shirt! 37 | 38 | ### Goals of the stakeholder program 39 | 40 | The goal of the stakeholder program is to make Rust's design process even more inclusive. We have observed that existing mechanisms like the RFC process or issue threads are often not a very good fit for certain categories of users, such as production users or the maintainers of large libraries, as they are not able to keep up with the discussion. As a result, they don't participate, and we wind up depriving ourselves of valuable feedback. The stakeholder program looks to supplement those mechanisms with direct contact. 41 | 42 | Another goal is to get more testing: one problem we have observed is that features are often developed and deployed on nightly, but production users don't really want to try them out until they hit stable! We would like to get some commitment from people to give things a try so that we have a better chance of finding problems before stabilization. 43 | 44 | We want to emphasize that we welcome design feedback from **all Rust users**, regardless of whether you are a named stakeholder or not. If you're using async Rust, or have read through the designs and have a question or idea for improvement, please feel free to [open an issue](https://github.com/rust-lang/async-fundamentals-initiative/issues/new/choose) and tell us about it! 45 | 46 | ### Number of stakeholder representatives 47 | 48 | We are selecting a small number of stakeholders covering various points in the design space, e.g. 49 | 50 | * Web services author 51 | * Embedded Rust 52 | * Web framework author 53 | * Web framework consumer 54 | * High-performance computing 55 | * Operating systems 56 | 57 | If you have thoughts or suggestions for good stakeholders, or you think that you yourself might be a good fit, please reach out to tmandry or nikomatsakis! 58 | -------------------------------------------------------------------------------- /roadmap.md: -------------------------------------------------------------------------------- 1 | # Async fn fundamentals 2 | 3 | This initiative is part of the overall [async vision roadmap](https://rust-lang.github.io/wg-async-foundations/vision/roadmap.html). 4 | 5 | ## Impact 6 | 7 | * Able to write `async fn` in traits and trait impls 8 | * Able to easily declare that `T: Trait + Send` where "every async fn in `Trait` returns a `Send` future" 9 | * Traits that use `async fn` can still be [dyn safe](./design-discussions/dyn_async_trait.md) though some tuning may be required 10 | * Async functions in traits desugar to [impl Trait in traits] 11 | * Able to write ["async fn drop"][async drop] to declare that the destructor may await 12 | * Support for [async closures] 13 | 14 | ## Milestones 15 | 16 | | Milestone | State | Key participants | 17 | | --- | --- | --- | 18 | | Author [evaluation doc] for [static async trait] | done 🎉 | [tmandry] 19 | | Author [evaluation doc] for [dyn async trait] | [done 🎉](https://rust-lang.github.io/async-fundamentals-initiative/evaluation/challenges/dyn_traits.html) | [tmandry] 20 | | Author [evaluation doc] for [async drop] | 💤 | 21 | | Author [evaluation doc] for [impl Trait in traits] | done | [tmandry] 22 | | [Stabilize] [type alias impl trait] | in-progress | [oli-obk] 23 | | [Stabilize] [generic associated types] | [done 🎉](https://github.com/rust-lang/rust/pull/96709) | [jackh726] 24 | | Author RFC for async fn in traits | [done 🎉](https://github.com/rust-lang/rfcs/pull/3185) | 25 | | Author [evaluation doc] for [async closures] | [blog](https://smallcultfollowing.com/babysteps/blog/2023/03/29/thoughts-on-async-closures/) [posts](https://smallcultfollowing.com/babysteps/blog/2023/05/09/giving-lending-and-async-closures/) authored, doc pending | [nikomatsakis] | 26 | | Author RFC for impl trait in traits | [done](https://github.com/rust-lang/rfcs/pull/3425) | 27 | | [Feature complete] for async fn in traits | done 🎉 | [compiler-errors] | 28 | | [Feature complete] for [impl Trait in traits] | done 🎉 | [compiler-errors] | 29 | | [Feature complete] for [async drop] | 💤 | 30 | | [Feature complete] for [async closures] | 💤 | 31 | | [Stabilize] async fn in traits | [proposed](https://github.com/rust-lang/rust/pull/115822) | [compiler-errors] | 32 | | [Stabilize] impl trait in traits | [proposed](https://github.com/rust-lang/rust/pull/115822) | [compiler-errors] | 33 | | [Stabilize] async drop | 💤 | 34 | | [Stabilize] async closures | 💤 | 35 | 36 | ## Design discussions 37 | 38 | This directory hosts notes on important design discussions along with their resolutions. 39 | In the table of contents, you will find the overall status: 40 | 41 | * ✅ -- **Settled!** Input only needed if you have identified a fresh consideration that is not covered by the write-up. 42 | * 💬 -- **Under active discussion.** Check the write-up, which may contain a list of questions or places where feedback is desired. 43 | * 💤 -- **Paused.** Not under active discussion, but we may be updating the write-up from time to time with details. 44 | 45 | [nikomatsakis]: https://github.com/nikomatsakis/ 46 | [oli-obk]: https://github.com/oli-obk/ 47 | [jackh726]: https://github.com/jackh726/ 48 | [tmandry]: https://github.com/tmandry/ 49 | [compiler-errors]: https://github.com/compiler-errors/ 50 | [async drop]: ./design-discussions/async_drop.md 51 | [async closures]: ./design-discussions/async_closures.md 52 | [impl Trait in traits]: ./design-discussions/impl_trait_in_traits.md 53 | [type alias impl trait]: https://github.com/rust-lang/rust/issues/63063 54 | [generic associated types]: https://github.com/rust-lang/generic-associated-types-initiative 55 | [static async trait]: ./design-discussions/static_async_trait.md 56 | [dyn async trait]: ./design-discussions/dyn_async_trait.md 57 | 58 | [evaluation doc]: https://rust-lang.github.io/wg-async-foundations/vision/how_to_vision/evaluations.html 59 | [stabilize]: https://lang-team.rust-lang.org/initiatives/process/stages/stabilized.html 60 | [feature complete]: https://lang-team.rust-lang.org/initiatives/process/stages/feature_complete.html 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # async fundamentals initiative 2 | 3 | ![initiative status: active](https://img.shields.io/badge/status-active-brightgreen.svg) 4 | 5 | {{#include ./badges.md}} 6 | 7 | ## What is this? 8 | 9 | This page tracks the work of the async fundamentals [initiative], part of the wg-async-foundations [vision process]! To learn more about what we are trying to do, and to find out the people who are doing it, take a look at the [charter]. 10 | 11 | [charter]: ./CHARTER.md 12 | [initiative]: https://lang-team.rust-lang.org/initiatives.html 13 | [vision process]: https://rust-lang.github.io/wg-async-foundations/vision.html 14 | 15 | ## Current status 16 | 17 | This is an **umbrella initiative** and, as such, it covers a number of subprojects. 18 | 19 | See the [roadmap](./roadmap.md) for a list of individual milestones and their status. 20 | 21 | | Subproject | Issue | Progress | State | Status | 22 | |-------------------------------|----------|--------------|-------|----------------| 23 | | async fn | [#50547] | ▰▰▰▰▰ | ✅ | ![stable][] | 24 | | [static async fn in trait] | [#91611] | ▰▰▱▱▱ | 🦀 | ![accepted rfc][] | 25 | | [dyn async fn in trait] | – | ▰▱▱▱▱ | 🦀 | ![planning rfc][] | 26 | | async drop | – | ▰▱▱▱▱ | 🦀 | ![not started][] | 27 | | async closures | – | ▰▱▱▱▱ | 💤 | ![not started][] | 28 | 29 | [static async fn in trait]: explainer/async_fn_in_traits.md 30 | [dyn async fn in trait]: explainer/async_fn_in_dyn_trait.md 31 | 32 | [#50547]: https://github.com/rust-lang/rust/issues/50547 33 | [#91611]: https://github.com/rust-lang/rust/issues/91611 34 | 35 | 39 | 40 | [Stage]: https://lang-team.rust-lang.org/initiatives/process/stages.html 41 | [Proposal]: https://lang-team.rust-lang.org/initiatives/process/stages/proposal.html 42 | [Experimental]: https://lang-team.rust-lang.org/initiatives/process/stages/experimental.html 43 | [Development]: https://lang-team.rust-lang.org/initiatives/process/stages/development.html 44 | [Feature complete]: https://lang-team.rust-lang.org/initiatives/process/stages/feature-complete.html 45 | [Stabilized]: https://lang-team.rust-lang.org/initiatives/process/stages/stabilized.html 46 | 47 | Key: 48 | 49 | * ✅ – phase complete 50 | * 🦀 – phase in progress 51 | * 💤 – phase not started yet 52 | 53 | ## How Can I Get Involved? 54 | 55 | * Check for 'help wanted' issues on this repository! 56 | * If you would like to help with development, please contact the [owner](./charter.md#membership) to find out if there are things that need doing. 57 | * If you would like to help with the design, check the list of active [design discussions](./design-discussions) first. 58 | * If you have questions about the design, you can file an issue, but be sure to check the [FAQ](./FAQ.md) or the [design discussions](./design-discussions) first to see if there is already something that covers your topic. 59 | * If you are using the feature and would like to provide feedback about your experiences, please [open a "experience report" issue][experience-report]. 60 | * If you are using the feature and would like to report a bug, please open a regular issue. 61 | 62 | We also participate on [Zulip][chat-link], feel free to introduce yourself over there and ask us any questions you have. 63 | 64 | [open issues]: /issues 65 | [experience-report]: https://github.com/rust-lang/async-fundamentals-initiative/issues/new?labels=experience-report&template=experience-report.md 66 | [chat-link]: https://rust-lang.zulipchat.com/#narrow/stream/187312-wg-async-foundations 67 | 68 | [team-toml]: https://github.com/rust-lang/team/blob/master/teams/wg-async-foundations.toml 69 | 70 | ## Building Documentation 71 | This repository is also an mdbook project. You can view and build it using the 72 | following command. 73 | 74 | ``` 75 | mdbook serve 76 | ``` 77 | -------------------------------------------------------------------------------- /evaluation/design/with_clauses.md: -------------------------------------------------------------------------------- 1 | # With clauses 2 | 3 | ## Status 4 | 5 | Crazy new idea that solves all problems 6 | 7 | ## Summary 8 | 9 | * Introduce a `with(x: T)` clause that can appear wherever where clauses can appear 10 | * These variables are in scope in any (non-const) code block that appears within those scopes. 11 | * Introduce a new `with(x = value) { ... }` expression 12 | * Within this block, you can invoke fuctions that have a `with(x: T)` clause (presuming that `value` is of type `T`); you can also invoke code which calls such functions transitively. 13 | * The values are propagated from the `with` block to the functions you invoke. 14 | 15 | ## More detailed look 16 | 17 | ### Simple example 18 | 19 | Suppose that we have a generic visitor interface in a crate `visit`: 20 | 21 | ```rust 22 | trait Visitor { 23 | fn visit(&self); 24 | } 25 | 26 | impl Visitor for Vec 27 | where 28 | V: Visitor, 29 | { 30 | fn visit(&self) { 31 | for e in self { 32 | e.visit(); 33 | } 34 | } 35 | } 36 | ``` 37 | 38 | I would like to use this interface in my crate. But I was hoping to increment a counter each time one of my types is visited. Unfortunately, the `Visitor` trait doesn't offer any way to thread access to this counter into the impl. With `with`, though, that's no problem. 39 | 40 | ```rust 41 | struct Context { counter: usize } 42 | 43 | struct MyNode { 44 | 45 | } 46 | 47 | impl Visitor for MyNode 48 | with(cx: &mut Context) 49 | { 50 | fn visit(&self) { 51 | cx.counter += 1; 52 | } 53 | } 54 | ``` 55 | 56 | Now I can use this visitor trait as normal: 57 | 58 | ```rust 59 | fn process_item() { 60 | let cx = Context { counter: 0 }; 61 | let v = vec![MyNode, MyNode, MyNode]; 62 | with(cx: &mut cx) { 63 | v.visit(); 64 | } 65 | assert_eq!(cx.counter, 3); 66 | } 67 | ``` 68 | 69 | ## How it works 70 | 71 | We extend the environment with a `with(name: Type)` clause. When we typecheck a `with(name: value) { ... }` statement, we enter those clauses into the environment. When we check impls that contain `with` clauses, they match against those clauses like any other where clause. 72 | 73 | After matching an impl, we are left with a "residual" of implicit parameters. When we monomorphize a function applied to some particular types, we will check the where clauses declared on the function against those types and collect the residual parameters. These are added to the function and supplied by the caller (which must have them in scope). 74 | 75 | ### Things to overcome 76 | 77 | Dyn value construction: we need some way to permit impls that use `with` to be made into `dyn` values. This is very hard, maybe impossible. The problem is that we don't 78 | want to "capture" the `with` values into the `dyn` -- so what do we do if somebody 79 | packages up a `Box` and puts it somewhere? 80 | 81 | We could require that context values implement `Default` but .. that stinks. =) 82 | 83 | We could panic. That kind of stinks too! 84 | 85 | We could limit to traits that are not dyn safe, particularly if there was a manual impl of dyn safety. The key problem is that, today, for a dyn safe trait, one can make a `dyn` trait without knowing the source type: 86 | 87 | ```rust 88 | fn foo(v: T) { 89 | let x: Box = Box::new(v); 90 | } 91 | ``` 92 | 93 | But, now, what happens if the `Box` is allowed to escape the `with` scope, and the methods are invoked? 94 | 95 | Conceivably we could leverage lifetimes to prevent this, but I'm not *exactly* sure how. It would imply a kind of "lifetime view" on the type `T` that ensures it is not considered to outlive the `with` scope. That doesn't feel right. What we *really* want to do is to put a lifetime bound of sorts on the... use of the where clause. 96 | 97 | We could also rework this in an edition, so that this capability is made more explicit. Then only traits and impls in the new edition would be able to use `with` clauses. This would harm edition interop to some extent, we'd have to work that out too. 98 | 99 | -------------------------------------------------------------------------------- /roadmap/static_async_trait_mvp.md: -------------------------------------------------------------------------------- 1 | # MVP: Static async fn in traits 2 | 3 | This section defines an initial **minimum viable product** (MVP). This MVP is meant to be a subset of async fns in traits that can be implemented and stabilized quickly. 4 | 5 | ## In a nutshell 6 | 7 | * In traits, `async fn foo(&self)` desugars to 8 | * an anonymous associated type `type Foo<'me>: Future` (as this type is anonymous, users cannot actually name it; the name `Foo` here is for demonstrative purposes only) 9 | * a function `fn foo(&self) -> Self::Foo<'_>` that returns this future 10 | * In impls, `async fn foo(&self)` desugars to 11 | * a value for the anonymous associated type `type Foo<'me> = impl Future` 12 | * a function `fn foo(&self) -> Self::Foo<'_> { async move { ... } }` 13 | * If the trait used `async fn`, then the impl must use `async fn` (and vice versa) 14 | * Traits that use `async fn` are *not* dyn safe 15 | * In the MVP, traits using async fn can only be used with `impl Trait` or generics 16 | 17 | ## What this enables 18 | 19 | * The MVP is sufficient for projects like [embassy], which already model async fns in traits in this way. 20 | * TODO: Once we have a list of stakeholders, try to get a sense for how many uses of [`async-trait`] could be replaced 21 | 22 | [embassy]: https://github.com/embassy-rs/embassy 23 | [`async-trait`]: https://crates.io/crates/async-trait 24 | 25 | ## Notable limitations and workarounds 26 | 27 | * No support for [`dyn`] 28 | * This is a fundamental limitation; the only workaround is to use [`async-trait`] 29 | * No ability to name the resulting futures: 30 | * This means that one cannot build non-generic adapters that reference those futures. 31 | * Workaround: define a function alongside the impl and use a TAIT for its return type 32 | * No ability to bound the resulting futures (e.g., to require that they are `Send`) 33 | * This rules out certain use cases when using work-stealing [executor styles], such as the [background logging] scenario. Note that many other uses of async fn in traits will likely work fine even with a work-stealing executor: the only limitation is that one cannot write generic code that invokes `spawn`. 34 | * Workaround: do the desugaring manually when required, which would give a name for the relevant future. 35 | 36 | [background logging]: ../evaluation/scenarios/background-logging.md 37 | [executor styles]: ../evaluation/executor-styles.md 38 | [`dyn`]: ../evaluation/challenges/dyn_traits.md 39 | 40 | ## Implementation plan 41 | 42 | * This MVP relies on having [generic associated types][gats] and [type alias impl trait][tait], but they are making good progress. 43 | * Otherwise, the implementation is a straightforward desugaring, similar to how inherent async fns are implemented 44 | * We may wish to also ship a variant of the [`async-trait`] macro that lets people easily experiment with this feature 45 | 46 | [gats]: https://rust-lang.github.io/generic-associated-types-initiative/ 47 | [tait]: https://rust-lang.github.io/impl-trait-initiative/ 48 | 49 | ## Forward compatibility 50 | 51 | The MVP sidesteps a number of the [more challenging design problems][challenges]. It should be forwards compatible with: 52 | 53 | * Adding support for [`dyn`] traits later 54 | * Adding a mechanism to [bound the resulting futures][bound] 55 | * **WARNING:** It is NOT compatible with [Implied Send], however! 56 | * Adding a mechanism to [name the resulting futures][name] 57 | * The futures added here are anonymous, and we can always add explicit names later. 58 | * *If* we were to [name the resulting futures after the methods][simple_names], and users had existing traits that used those same names already, this could present a conflict, but one that could be resolved. 59 | * Supporting and bounding [async drop] 60 | * This trait will not exist yet with the MVP, and supporting `async fn` doesn't enable anything fundamental that we don't have to solve anyway. 61 | 62 | [challenges]: ../evaluation/challenges.md 63 | [bound]: ../evaluation/challenges/bounding_futures.md 64 | [Implied Send]: ../evaluation/design/implied_send.md 65 | [async drop]: ../evaluation/challenges/bounding_async_drop.md 66 | [name]: ../evaluation/challenges/naming_futures.md 67 | [simple_names]: ../evaluation/design/simple_names.md 68 | -------------------------------------------------------------------------------- /evaluation/design/dynx/creation.md: -------------------------------------------------------------------------------- 1 | # How do you create a dynx? 2 | 3 | 4 | In the previous section, we showed how a `#[dyn(identity)]` function must return "something that can be converted into a dynx struct", and we showed that a case of returning a `Pin>` type. But what are the general rules for constructing a `dynx` struct? You're asking the right question, but that's a part of the design we haven't bottomed out yet. 5 | 6 | In short, there are two "basic" approaches we could take. One of them is more conservative, in that it doesn't change much about Rust today, but it's also much more complex, because `dyn` dealing with all the "corner cases" of `dyn` is kind of complicated. The other is more radical, but may result in an overall smoother, more coherent design. 7 | 8 | Apart from that tantalizing tidbit, we are intentionally not providing the details here, because this document is long enough as it is! The next document dives into this question, along with a related question, which is how `dynx` and sealed traits interact. 9 | 10 | This is actually a complex question with (at least) two possible answers. 11 | 12 | ## Alternative A: P must deref to something that implements Bounds 13 | 14 | The pointer type `P` must implement `IntoRawPointer` (along with various other criteria) and its referent must implement `Bounds`. 15 | 16 | ### IntoRawPointer trait 17 | 18 | ```rust 19 | // Not pseudocode, will be added to the stdlib and implemented 20 | // by various types, including `Box` and `Pin`. 21 | 22 | unsafe trait IntoRawPointer: Deref { 23 | /// Convert this pointer into a raw pointer to `Self::Target`. 24 | /// 25 | /// This raw pointer must be valid to dereference until `drop_raw` (below) is invoked; 26 | /// this trait is unsafe because the impl must ensure that to be true. 27 | fn into_raw(self) -> *mut Self::Target; 28 | 29 | /// Drops the smart pointer itself as well as the contents of the pointer. 30 | /// For example, when `Self = Box`, this will free the box as well as the 31 | /// `T` value. 32 | unsafe fn drop_raw(this: *mut Self::Target); 33 | } 34 | ``` 35 | 36 | ### Other conditions pointer must meet 37 | 38 | * Must be `IntoRawPointer` which ensures: 39 | * `Deref` and `DerefMut` are stable, side-effect free and all that 40 | * they deref to the same memory as `into_raw` 41 | * If `Bounds` includes a `&mut self` method, `P` must be `DerefMut` 42 | * If `Bounds` includes a `&self` method, `P` must be `Deref` 43 | * If `Bounds` includes `Pin<&mut Self>`, `P` must be `Unpin` ... and ... something something `DerefMut`? how do you get from `Pin

` to `Pin<&mut P::Target>`? 44 | * If `Bounds` includes `Pin<&Self>`, `P` must be `Unpin` ... and ... something something `DerefMut`? how do you get from `Pin

` to `Pin<&mut P::Target>`? 45 | * If `Bounds` includes an auto trait `AutoTrait`, `P` must implement `AutoTrait` 46 | * and: `dynx Bounds` implements the auto trait `AutoTrait` (in general, `dynx Bounds` implements all of `Bounds`) 47 | * `Bounds` must be "dyn safe" 48 | 49 | ## Alternative B: P must implement Bounds 50 | 51 | Alternatively, we could declare that the pointer type P must implement `Bounds`. This is much simpler to express, but it has some issues. For example, if you have 52 | 53 | ```rust 54 | trait Foo { 55 | 56 | } 57 | ``` 58 | 59 | then we could not construct a `dynx Foo` from a `Box` because there is no `impl Foo for Box`. It would be nice if those impls could be added automatically or at least more easily. 60 | 61 | ```rust 62 | // Not pseudocode, will be added to the stdlib and implemented 63 | // by various types, including `Box` and `Pin`. 64 | 65 | unsafe trait IntoRawPointer: Deref { 66 | /// Convert this pointer into a raw pointer to `Self::Target`. 67 | /// 68 | /// This raw pointer must be valid to dereference until `drop_raw` (below) is invoked; 69 | /// this trait is unsafe because the impl must ensure that to be true. 70 | fn into_raw(self) -> *mut Self::Target; 71 | 72 | /// These methods would be used by compiler to convert back so we can invoke the original 73 | /// impls. 74 | unsafe fn from_ref(this: &*mut Self::Target) -> &Self; 75 | unsafe fn from_mut_ref(this: &mut *mut Self::Target) -> &mut Self; 76 | ... 77 | 78 | /// Drops the smart pointer itself as well as the contents of the pointer. 79 | /// For example, when `Self = Box`, this will free the box as well as the 80 | /// `T` value. 81 | unsafe fn drop_raw(this: *mut Self::Target); 82 | } 83 | ``` 84 | -------------------------------------------------------------------------------- /explainer/async_fn_in_dyn_trait/avoiding_allocation.md: -------------------------------------------------------------------------------- 1 | # Using dyn without allocation 2 | 3 | ![planning rfc][] 4 | 5 | {{#include ../../badges.md}} 6 | 7 | In the previous chapter, we showed how you can invoke async methods from a `dyn Trait` value in a natural fashion. In those examples, though, we assume that it was ok to allocate a `Box` for every call to an async function. For most applications, this is true, but for some applications, it is not. This could be because they intend to run in a kernel or embedded context, where no allocator is available, or it could be because of a very tight loop in which allocation introduces too much overhead. The good news is that our design allows you to avoid using Box, though it does take a bit of work on your part. 8 | 9 | In general, functions that accept a `&dyn Trait` as argument don't control how memory is allocated. So the `count` function that we saw before can be used equally well on a no-std or kernel platform: 10 | 11 | ```rust 12 | async fn count(iter: &mut dyn AsyncIterator) { 13 | // Whether or not `iter.next()` will allocate a box 14 | // depends on the underlying type; this `count` fn 15 | // doesn't have to know, and so it works equally 16 | // well in a no-std or std environment. 17 | ... 18 | } 19 | ``` 20 | 21 | The decision about whether to use box or some other way of returning a future is made by the type implementing the async trait. In [the previous example](./how_it_feels.md), the type was `YieldingRangeIterator`, and its impl didn't make any kind of explicit choice, and thus the default is that it will allocate a `Box`: 22 | 23 | ```rust 24 | impl AsyncIterator for YieldingRangeIterator { 25 | type Item = u32; 26 | 27 | async fn next(&mut self) { 28 | // The default behavior here is to allocate a `Box` 29 | // when `next` is called through a `dyn AsyncIterator` 30 | // (no `Box` is allocated when `next` is called through 31 | // static dispatch, in that case the future itself is 32 | // returned.) 33 | ... 34 | } 35 | } 36 | ``` 37 | 38 | If you want to use `YieldingRangeIterator` in a context without `Box`, you can do that by wrapping it in an *adapter* type. This adapter type will implement an alternative memory allocation strategy, such as using pre-allocated stack storage. 39 | 40 | For the most part, there is no need to implement your own adapter type, beacuse the `dyner` crate (to be published by rust-lang) includes a number of useful ones. For example, to pre-allocate the `next` future on the stack, which is useful both for performance or no-std scenarios, you could use an "inline" adapter type, like the `InlineAsyncIterator` type provided by the `dyner` crate: 41 | 42 | ```rust 43 | use dyner::InlineAsyncIterator; 44 | // ^^^^^^^^^^^^^^^^^^^ 45 | // Inline adapter type 46 | 47 | async fn count_range(mut x: YieldingRangeIterator) -> usize { 48 | // allocates stack space for the `next` future: 49 | let inline_x = InlineAsyncIterator::new(x); 50 | 51 | // invoke the `count` fn, which will no use that stack space 52 | // when it runs 53 | count(&mut inline_x).await 54 | } 55 | ``` 56 | 57 | Dyner provides some other strategies, such as the `CachedAsyncIterator` (which caches the returned `Box` and re-uses the memory in between calls) and the `BoxInAllocatorAsyncIterator` (which uses a `Box`, but with a custom allocator). 58 | 59 | ## How you apply an existing "adapter" strategy to your own traits 60 | 61 | The `InlineAsyncIterator` adapts an `AsyncIterator` to pre-allocate stack space for the returned futures, but what if you want to apply that inline stategy to one of your traits? You can do that by using the `#[inline_adapter]` attribute macro applied to your trait definition: 62 | 63 | ```rust 64 | #[inline_adapter(InlineMyTrait)] 65 | trait MyTrait { 66 | async fn some_function(&mut self); 67 | } 68 | ``` 69 | 70 | This will create an adapter type called `InlineMyTrait` (the name is given as an argument to the attribute macro). You would then use it by invoking `new`: 71 | 72 | ```rust 73 | fn foo(x: impl MyTrait) { 74 | let mut w = InlineMyTrait::new(x); 75 | bar(&mut w); 76 | } 77 | 78 | fn bar(x: &mut dyn MyTrait) { 79 | x.some_function(); 80 | } 81 | ``` 82 | 83 | If the trait is not defined in your crate, and hence you cannot use an attribute macro, you can use this alternate form, but it requires copying the trait definition: 84 | 85 | ```rust 86 | dyner::inline::adapter_struct! { 87 | struct InlineAsyncIterator for trait MyTrait { 88 | async fn foo(&mut self); 89 | } 90 | } 91 | ``` 92 | -------------------------------------------------------------------------------- /evaluation/design/custom_dyn_impls.md: -------------------------------------------------------------------------------- 1 | # Custom dyn impls 2 | 3 | As described in [dyn traits], `dyn Trait` types cannot include the types of each future without defeating their purpose; but outside of a `dyn` context, we *want* those associated types to have unique values for each impl. Threading this needle requires extending Rust so that the value of an associated type can be different for a `dyn Trait` and for the underlying impl. 4 | 5 | [dyn traits]: ../challenges/dyn_traits.md 6 | 7 | ## How it works today 8 | 9 | Conceptually, today, there is a kind of "generated impl" for each trait. This impl implements each method by indirecting through the vtable, and it takes the value of associated types from the dyn type: 10 | 11 | ```rust 12 | trait Foo { 13 | type Bar; 14 | 15 | fn method(&self); 16 | } 17 | 18 | impl Foo for dyn Foo { 19 | type Bar = B; 20 | 21 | fn method(&self) { 22 | let f: fn(&Self) = get_method_from_vtable(self) 23 | f(self) 24 | } 25 | } 26 | ``` 27 | 28 | Meanwhile, at the point where a type (say `u32`) is coerced to a `dyn Foo`, we generate a vtable based on the impl: 29 | 30 | ```rust 31 | // Given 32 | impl Foo for u32 { 33 | fn method(self: &u32) { XXX } 34 | } 35 | 36 | // we could a compile for `method`: 37 | // fn `::method`(self: &u32) { XXX } 38 | 39 | fn eg() { 40 | let x: u32 = 22; 41 | &x as &dyn Foo // <-- this case 42 | } 43 | 44 | // generates a vtable with a pointer to that method: 45 | // 46 | // Vtable_Foo = [ ..., `::method` ] 47 | ``` 48 | 49 | Note that there are some known problems here, such as [soundness holes in the coherence check](https://github.com/rust-lang/lang-team/blob/master/design-meeting-minutes/2020-01-13-dyn-trait-and-coherence.md). 50 | 51 | ## Rough proposal 52 | 53 | What we would like is the ability for this "dyn" impl to diverge more from the underlying impl. For example, given a trait `Foo` with an `async` fn method: 54 | 55 | ```rust 56 | trait Foo { 57 | async fn method(&self); 58 | } 59 | ``` 60 | 61 | The compiler might generate an impl like the following: 62 | 63 | ```rust 64 | impl Foo for dyn Foo { 65 | // ^^^^^^^ note that this type doesn't include Bar = ... 66 | 67 | type Bar = Box>; 68 | // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ because the result is hardcoded 69 | 70 | fn method(&self) -> Box> { 71 | let f: fn(&Self) = get_method_from_vtable(self) 72 | f(self) 73 | } 74 | } 75 | ``` 76 | 77 | The vtable, meanwhile, resembles what we had before, except that it doesn't point directly to `::method`, but rather to a wrapper function (let's call it `methodX`) that has the job of coercing from the concrete type into a `Box`: 78 | 79 | ``` 80 | // Vtable_Foo = [ ..., `::methodX`] 81 | // fn `::method`(self: &u32) { XXX } 82 | // fn `::methodX`(self: &u32) -> Box { Box::new(TheFuture) } 83 | ``` 84 | 85 | ## Auto traits 86 | 87 | To handle "auto traits", we need multiple impls. For example, assuming we adopted [trait multiplication](./trait_multiplication.md), we would have multiple impls, one for `dyn Foo` and one for `dyn Foo * Send`: 88 | 89 | 90 | ```rust 91 | trait Foo { 92 | async fn method(&self); 93 | } 94 | 95 | impl Foo for dyn Foo { 96 | type Bar = Box>; 97 | 98 | fn method(&self) -> Box> { 99 | 100 | } 101 | } 102 | 103 | impl Foo for dyn Foo * Send { 104 | // ^^^^^^^^^^^^^^ 105 | 106 | type Bar = Box + Send>; 107 | // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 108 | 109 | fn method(&self) -> Box> { 110 | .... 111 | } 112 | } 113 | 114 | // compiles to: 115 | // 116 | // Vtable_Foo = [ ..., `::methodX`] 117 | // fn `::method`(self: &u32) { XXX } 118 | // fn `::methodX`(self: &u32) -> Box { Box::new(TheFuture) } 119 | ``` 120 | 121 | ### Hard-coding box 122 | 123 | One challenge is that we are hard-coding `Box` in the above impls. We could control this in a number of ways: 124 | 125 | * Annotate the trait with an alternate wrapper type 126 | * Extend `dyn` types with some kind of indicator of the wrapper (`dyn(Box)`) that they use for this case 127 | * Generate impls for `Box` -- has several shortcomings 128 | 129 | ### Applicable 130 | 131 | Everything here is applicable more broadly, for example to types that return `Iterator`. 132 | 133 | It'd be nice if we extended this capability of "writing your own dyn impls" to end-users. -------------------------------------------------------------------------------- /evaluation/case-studies/embassy.md: -------------------------------------------------------------------------------- 1 | # Use of AFIT in Embaassy 2 | 3 | *The following are rough notes on the usage of Async Function in Traits from the [Embassy][] runtime. They are derived from a conversation between dirbaio and nikomatsakis.* 4 | 5 | [Embassy]: https://github.com/embassy-rs/embassy 6 | 7 | Links to uses of async functions in traits within Embassy: 8 | 9 | * most popular ones are [embedded-hal-async](https://github.com/rust-embedded/embedded-hal/tree/master/embedded-hal-async/src) 10 | * HAL crates provide impls for particular microcontrollers (e.g., [gpiote](https://github.com/embassy-rs/embassy/blob/master/embassy-nrf/src/gpiote.rs#L518), [spim](https://github.com/embassy-rs/embassy/blob/master/embassy-nrf/src/spim.rs#L523), [i2c](https://github.com/embassy-rs/embassy/blob/master/embassy-stm32/src/i2c/v2.rs#L1061)) 11 | * driver crates use the traits to [implement a driver for some chip that works on top of any HAL: 12 | * [nrf70](https://github.com/embassy-rs/nrf70/blob/main/src/main.rs#L811) (that one is interesting because it defines another async Bus trait on top, because that chip can be used with either SPI or QSPI) 13 | * [es-wifi-driver](https://github.com/drogue-iot/es-wifi-driver/blob/main/src/lib.rs#L132) 14 | * [hts221](https://github.com/drogue-iot/hts221-async/blob/main/src/lib.rs#L40) 15 | * [sx127x](https://github.com/embassy-rs/embassy/blob/master/embassy-lora/src/sx127x/sx127x_lora/mod.rs#L50) 16 | * there's also embedded-io, which is [std::io traits adapted for embedded](https://github.com/embassy-rs/embedded-io/blob/master/src/asynch.rs) 17 | * HALs have [impls for serial ports](https://github.com/embassy-rs/embassy/blob/master/embassy-nrf/src/buffered_uarte.rs#L600) 18 | * embassy-net has an [impl for TCP sockets](https://github.com/embassy-rs/embassy/blob/master/embassy-net/src/tcp.rs#L431) 19 | * here's some [driver using it](https://github.com/drogue-iot/esp8266-at-driver/blob/main/src/lib.rs#L74) 20 | * embassy-usb has a [Driver trait](https://github.com/embassy-rs/embassy/blob/master/embassy-usb-driver/src/lib.rs); that one is probably the most complex, it's an async trait with associated types with more async traits, and interesting lifetimes 21 | * HALs [impl these traits for one particular chip](https://github.com/embassy-rs/embassy/blob/master/embassy-nrf/src/usb/mod.rs#L178) and embassy-usb uses them to [implement a chip-independent USB stack](https://github.com/embassy-rs/embassy/blob/master/embassy-usb/src/lib.rs#L188) 22 | 23 | 24 | most of these are "abstract over hardware", and when you build a firmware for some product/board you know which actual hardware you have, so you use static generics, no need for dyn 25 | 26 | the few instances I've wished for dyn is: 27 | * with embedded-io it does sometimes happen. For example, running the same terminal ui over a physical serial port and over telnet at the same time. Without dyn that code gets monomorphized two times, which is somewhat wasteful. 28 | * this trait https://github.com/embassy-rs/embassy/blob/master/embassy-usb/src/lib.rs#L89 . That one MUST use dyn because you want to register multiple handlers that might be different types. Sometimes it'd have been handy to be able to do async things within these callbacks. Workaround is to fire off a notification to some other async task, it's not been that bad. 29 | * niko: how is this used? 30 | * handlers are added [here](https://github.com/embassy-rs/embassy/blob/master/embassy-usb/src/builder.rs#L262), passed into UsbDevice [here](https://github.com/embassy-rs/embassy/blob/master/embassy-usb/src/lib.rs#L225), and then called when handling some bus-related stuff, for example [here](https://github.com/embassy-rs/embassy/blob/master/embassy-usb/src/lib.rs#L714-L715). 31 | * the tldr of what it's used for is you might have a "composite" usb device, which can have multiple "classes" at the same time (say, an Ethernet adapter and a serial port). Each class gets its own "endpoints" for data, so each launches its own independent async tasks reading/writing to these endpoints. 32 | * But there's also a "control pipe" endpoint that carries "control" requests that can be for any class for example for ethernet there's control requests for "bring the ethernet interface up/down", so each class registers a handler with callbacks to handle their own control requests, there's a "control pipe" task that dispatches them. 33 | * Sometimes when handling them, you want to do async stuff. For example for "bring the ethernet interface up" you might want to do some async SPI transfer to the ethernet chip, but currently you can't. 34 | * niko: would all methods be async if you could? 35 | * not sure if all methods, but probably `control_in`/`control_out` yes. and about where to store the future for the dyn... not sure. That crate is no-alloc so Box is out it'd probably be inline in the stack, like with StackFuture. Would need configuring the max size, probably some compile-time setting, or a const-generic in UsbDevice. 36 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [👋 Welcome](./README.md) 4 | - [✏️ Updates](./updates.md) 5 | - [2021-Oct](./updates/2021-oct.md) 6 | - [📜 Charter](./CHARTER.md) 7 | - [👪 Stakeholders](./stakeholders.md) 8 | - [2021 Nov](./stakeholders/2021-nov.md) 9 | - [2022 Sep](./stakeholders/2022-sep.md) 10 | - [🛣 Roadmap](./roadmap.md) 11 | - [💬 Static async trait](./roadmap/static_async_trait.md) 12 | - [MVP: Static async fn in traits](./roadmap/static_async_trait_mvp.md) 13 | - [💬 impl Trait in traits](./roadmap/impl_trait_in_traits.md) 14 | - [💬 Dyn async trait](./roadmap/dyn_async_trait.md) 15 | - [💤 Dyn trait](./roadmap/dyn_trait.md) 16 | - [💤 Async drop](./roadmap/async_drop.md) 17 | - [💤 Async closures](./roadmap/async_closures.md) 18 | - [🔬 Evaluation](./evaluation.md) 19 | - [Executor styles](./evaluation/executor-styles.md) 20 | - [Reference scenarios](./evaluation/scenarios.md) 21 | - [Background logging](./evaluation/scenarios/background-logging.md) 22 | - [Implementing AsyncRead](./evaluation/scenarios/implementing-async-read.md) 23 | - [Dyn](./evaluation/scenarios/dyn.md) 24 | - [Embedded system consuming general purpose libraries](./evaluation/scenarios/dyn/embedded-consume.md) 25 | - [Performance-sensitive inner loop with dynamic dispatch](./evaluation/scenarios/dyn/inner-loop.md) 26 | - [Taking ownership of the receiver](./evaluation/scenarios/dyn/taking-ownership.md) 27 | - [Async drop](./evaluation/scenarios/dyn/async-drop.md) 28 | - [Embedded async drop](./evaluation/scenarios/dyn/embedded-async-drop.md) 29 | - [Reference scenarios](./evaluation/scenarios/async-fn-in-traits.md) 30 | - [Challenges](./evaluation/challenges.md) 31 | - [Bounding futures](./evaluation/challenges/bounding_futures.md) 32 | - [Naming futures](./evaluation/challenges/naming_futures.md) 33 | - [Dyn traits](./evaluation/challenges/dyn_traits.md) 34 | - [Bounding async drop](./evaluation/challenges/bounding_async_drop.md) 35 | - [Guaranteeing async drop](./evaluation/challenges/guaranteeing_async_drop.md) 36 | - [Implicit await with async drop](./evaluation/challenges/implicit_await_with_async_drop.md) 37 | - [Design documents](./evaluation/design.md) 38 | - [Implied Send](./evaluation/design/implied_send.md) 39 | - [Trait multiplication](./evaluation/design/trait_multiplication.md) 40 | - [Inline async fn](./evaluation/design/inline_async_fn.md) 41 | - [Custom dyn impls](./evaluation/design/custom_dyn_impls.md) 42 | - [Auto traits consider AsyncDrop](./evaluation/design/auto_traits_consider_async_drop.md) 43 | - [Simple names](./evaluation/design/simple_names.md) 44 | - [Bound items](./evaluation/design/bound_items.md) 45 | - [With clauses](./evaluation/design/with_clauses.md) 46 | - [Dynx trait](./evaluation/design/dynx.md) 47 | - [Creation](./evaluation/design/dynx/creation.md) 48 | - [With auto traits](./evaluation/design/dynx/auto_trait.md) 49 | - [Sealed traits](./evaluation/design/dynx/sealed_traits.md) 50 | - [Case studies](./evaluation/case-studies.md) 51 | - [Builder + Provider API](./evaluation/case-studies/builder-provider-api.md) 52 | - [Socket Handler Refactor](./evaluation/case-studies/socket-handler.md) 53 | - [Tower](./evaluation/case-studies/tower.md) 54 | - [Microsoft AFIT](./evaluation/case-studies/microsoft.md) 55 | - [Use of AFIT in Embassy](./evaluation/case-studies/embassy.md) 56 | - [📚 Explainer](./explainer.md) 57 | - [User guide from the future](./explainer/user_guide_future.md) 58 | - [Async fn in traits](./explainer/async_fn_in_traits.md) 59 | - [Async fn in dyn trait](./explainer/async_fn_in_dyn_trait.md) 60 | - [How it feels to use](./explainer/async_fn_in_dyn_trait/how_it_feels.md) 61 | - [Using dyn without allocation](./explainer/async_fn_in_dyn_trait/avoiding_allocation.md) 62 | - [How it works](./explainer/async_fn_in_dyn_trait/how_it_works.md) 63 | - [Hardcoding box](./explainer/async_fn_in_dyn_trait/hardcoding_box.md) 64 | - [Generalizing from box to dynx](./explainer/async_fn_in_dyn_trait/generalizing_from_box_to_dynx.md) 65 | - [Identity shim functions](./explainer/async_fn_in_dyn_trait/identity_shim_functions.md) 66 | - [Nested `impl Trait`](./explainer/async_fn_in_dyn_trait/nested_impl_trait.md) 67 | - [Unresolved questions](./explainer/async_fn_in_dyn_trait/unresolved.md) 68 | - [Future possibilities](./explainer/async_fn_in_dyn_trait/future_possibilities.md) 69 | - [Appendix: Summary of user-facing extensions](./explainer/user_facing_summary.md) 70 | - [Appendix: Implementation plan](./explainer/implementation_plan.md) 71 | - [Appendix: Inline async iter adapter](./explainer/inline_async_iter_adapter.md) 72 | - [✨ RFC](./RFC.md) 73 | - [Static async fn in traits](./RFC/static_async_fn_in_traits.md) 74 | - [Refined trait impls](./RFC/refined_impls.md) 75 | - [Return position `impl Trait` in traits](./RFC/return_position_impl_trait_in_traits.md) 76 | - [😕 FAQ](./FAQ.md) 77 | - [Archive](./archive.md) 78 | - [2021: Phase 1](./archive/2021-phase_1.md) 79 | - [2021: Phase 1 narrative](./archive/2021-phase_1_narrative.md) 80 | -------------------------------------------------------------------------------- /evaluation/design/implied_send.md: -------------------------------------------------------------------------------- 1 | # Implied Send 2 | 3 | ## Status 4 | 5 | ❌ Rejected. This idea can be quite [productive](https://rustacean-principles.netlify.app/how_rust_empowers/productive.html), but it is not [versatile](https://rustacean-principles.netlify.app/how_rust_empowers/versatile.html) (it rules out important use cases) and it is not [supportive](https://rustacean-principles.netlify.app/how_rust_empowers/supportive.html) (it is confusing). 6 | 7 | (FIXME: I think the principles aren't quite capturing the constriants here! We should adjust.) 8 | 9 | ## Summary 10 | 11 | Targets the [bounding futures](../challenges/bounding_futures.md) challenge. 12 | 13 | The core idea of **"implied Send"** is to say that, by default at least, the future that results from an `async fn` must be `Send` if the `Self` type that implements the trait is `Send`. 14 | 15 | In Chalk terms, you can think of this as a bound like 16 | 17 | ``` 18 | if (Implemented(Self: Send)) { 19 | Implemented(Future: Send) 20 | } 21 | ``` 22 | 23 | Mathematically, this can be read as `Implemented(Self: Send) => Implemented(Future: Send)`. In other words, if you assume that `Self: Send`, then you can show that `Future: Send`. 24 | 25 | ## Desugared semantics 26 | 27 | If we extended the language with `if` bounds a la Chalk, then the desugared semantics of "implied send" would be something like this: 28 | 29 | ```rust 30 | trait AsyncIterator { 31 | type Item; 32 | type NextFuture: Future 33 | + if (Self: Send) { Send }; 34 | 35 | fn next(&mut self) -> impl Self::NextFuture; 36 | } 37 | ``` 38 | 39 | As a result, when you implement `AsyncIterator`, the compiler will check that your futures are `Send` if your input type is assumed to be `Send`. 40 | 41 | ## What's great about this 42 | 43 | The cool thing about this is that if you have a bound like `T: AsyncIterator + Send`, that automatically implies that any futures that may result from calling `AsyncIterator` methods will also be `Send`. Therefore, the [background logging](../scenarios/background-logging.md) scenario works like this, which is perfect for a [work stealing](../executor-styles.md) executor style. 44 | 45 | ```rust 46 | async fn start_writing_logs( 47 | logs: impl AsyncIterator + Send + 'static 48 | ) { 49 | ... 50 | } 51 | ``` 52 | 53 | ## What's not so great 54 | 55 | ### Negative reasoning: Semver interactions, how to prove 56 | 57 | In this proposal, when one implements an async trait for some concrete type, the compiler would presumably have to first check whether that type implements `Send`. If not, then it is ok if your futures do not implement `Send`. That kind of negative reasoning is actually quite tricky -- it has potential semver implications, for example -- although auto traits are more amenable to it than other things, since they already interact with semver in complex ways. 58 | 59 | In fact, if we use the standard approach for proving implication goals, the setup would not work at all. The typical approach to proving an implication goal like `P => Q` is to assume `P` is true and then try to prove `Q`. But that would mean that we would just wind up assuming that the `Self` type is `Send` and trying to use that to prove the resulting `Future` is `Send`, not *checking* whether `Self` is `Send` to decide. 60 | 61 | ### Not analogous to async fn outside of traits 62 | 63 | With inherent async functions, we don't check whether the resulting future is `Send` right away. Instead, we remember what state it has access to, and then if there is some part of the code that requires a future to be `Send`, we check *then*. 64 | 65 | But this "implied send" approach is different: the trait is effectively declaring up front that async functions must be send (if the Self is send, at least), and so you wind up with errors at the *impl*. This is true regardless of whether the future ever winds up being used in a spawn. 66 | 67 | The concern here is not *precisely* that the result is too strict (that's covered in the next bullet), but rather that it will be surprising behavior for people. They'll have a hard time understanding why they get errors about `Send` in some cases but not others. 68 | 69 | ### Stricter than is required for non-work-stealing [executor styles] 70 | 71 | Building on the previous point, this approach can be stricter than what is required when not using a work stealing [executor style]. 72 | 73 | As an example, consider a case where you are coding in a thread-local setting, and you have a struct like the following 74 | 75 | ```rust 76 | struct MyCustomIterator { 77 | start_index: u32 78 | } 79 | ``` 80 | 81 | Now you try to implement `AsyncIterator`. You know your code is thread-local, so you decide to use some `Rc` data in the process: 82 | 83 | ```rust 84 | impl AsyncIterator for MyCustomIterator { 85 | async fn read(&mut self, buf: &mut [u8]) -> io::Result { 86 | let ssh_key = Rc::new(vec![....]); 87 | do_some_stuff(ssh_key.clone()); 88 | something_else(self.start_index).await; 89 | } 90 | } 91 | ``` 92 | 93 | But now you get a compilation error: 94 | 95 | ``` 96 | error: `read` must be `Send`, since `MyCustomIterator` is `Send` 97 | ``` 98 | 99 | Frustrating! 100 | 101 | [executor styles]: ../executor-styles.md 102 | [executor style]: ../executor-styles.md 103 | -------------------------------------------------------------------------------- /evaluation/scenarios/dyn/embedded-consume.md: -------------------------------------------------------------------------------- 1 | # Embedded system consuming general purpose libraries 2 | 3 | ## Summary 4 | 5 | * General purpose library defines a trait `Trait` that use async fn 6 | * Embedded library can write a function `consume` that take `&mut dyn Trait` or `&dyn Trait` 7 | * Embedded library can call `consume` without requiring an allocator 8 | * It does have to jump through some "reasonable" hoops to specify the strategy for allocating the future, and it may not work for all possible traits 9 | * Can choose from: 10 | * pre-allocating storage space on the caller stack frame for resulting futures 11 | * creating an enum that chooses from all possible futures 12 | * The (admittedly vaporware) portability lint can help people discover this 13 | 14 | ## Status quo 15 | 16 | Grace is working on an embedded system. She needs to parse data from an input stream that is formatted as a series of packets in the format TLA. She finds a library `tla` on crates.io with a type that implements the async iterator trait: 17 | 18 | ```rust 19 | pub struct TlaParser { ... } 20 | 21 | #[async_trait] 22 | impl AsyncIterator for TlaParser { 23 | type Item = TlaPacket; 24 | async fn next(&mut self) -> Option { 25 | ... 26 | } 27 | } 28 | ``` 29 | 30 | Unfortunately, because `async_trait` desugars to something which uses `Box` internally, she can't use it: she's trying to write for a system with no allocator at all! 31 | 32 | *Note:* The *actual* status quo is that the `Stream` trait is not available in std, and the one in the futures crate uses a "poll" method which would be usable by embedded code. But we're looking to a future where we use `async fn` in traits specifically. 33 | 34 | ## Shiny future 35 | 36 | Grace is working on an embedded system. She needs to parse data from an input stream that is formatted as a series of packets in the TLA format. She finds a library `tla` on crates.io with a type that implements the async iterator trait: 37 | 38 | ```rust 39 | pub struct TlaParser { ... } 40 | 41 | impl AsyncIterator for TlaParser { 42 | type Item = TlaPacket; 43 | async fn next(&mut self) -> Option { 44 | ... 45 | } 46 | } 47 | ``` 48 | 49 | She has a function that is called from a number of places in the codebase: 50 | 51 | ```rust 52 | fn example_caller() { 53 | let mut tla_parser = TlaParser::new(SomeSource); 54 | process_packets(&mut tla_parser); 55 | } 56 | 57 | fn process_packets(parser: &mut impl AsyncIterator) { 58 | while let Some(packet) = parser.next().await { 59 | process_packet(packet); 60 | } 61 | } 62 | ``` 63 | 64 | As she is developing, she finds that `process_packets` is being monomorphized many times and it's becoming a significant code size problem for her. She decides to change to `dyn` to avoid that: 65 | 66 | ```rust 67 | fn process_packets(parser: &mut dyn AsyncIterator) { 68 | while let Some(packet) = parser.next().await { 69 | process_packet(packet); 70 | } 71 | } 72 | ``` 73 | 74 | ### Tackling portability by preallocating 75 | 76 | However, now her code no longer builds! She's getting an error from the portability lint: it seems that invoking `parser.next()` is allocating a box to return the future, and she has specified that she wants to be compatible with "no allocator": 77 | 78 | ``` 79 | warning: converting this type to a `dyn AsyncIterator` requires an allocator 80 | 3 | process_packets(&mut tla_parser); 81 | | ^^^^^^^^^^^^^^^ 82 | help: the `dyner` crate offer various a `PreAsyncIterator` wrapper type that can use stack allocation instead 83 | ``` 84 | 85 | Following the recommendations of the portability lint, she investigates the rust-lang `dyner` crate. In there she finds a few adapters she can use to avoid allocating a box. She decides to use the "preallocate" adapter, which preallocates stack space for each of the async functions she might call. To use it, she imports the `PreAsyncIterator` struct (provided by `dyner`) and wraps the `tla_parser` in it. Now she can use `dyn` without a problem: 86 | 87 | ```rust 88 | use dyner::preallocate::PreAsyncIterator; 89 | 90 | fn example_caller() { 91 | let tla_parser = TlaParser::new(SomeSource); 92 | let mut tla_parser = PreAsyncIterator::new(tla_parser); 93 | process_packets(&mut tla_parser); 94 | } 95 | 96 | fn process_packets(parser: &mut dyn AsyncIterator) { 97 | while let Some(packet) = parser.next().await { 98 | process_packet(packet); 99 | } 100 | } 101 | ``` 102 | 103 | ### Preallocated versions of her own traits 104 | 105 | As Grace continues working, she finds that she also needs to use `dyn` with a trait of her own devising: 106 | 107 | ```rust 108 | trait DispatchItem { 109 | async fn dispatch_item(&mut self) -> Result<(), DispatchError>; 110 | } 111 | 112 | struct MyAccumulatingDispatcher { } 113 | 114 | impl MyAccumulatingDispatcher { 115 | fn into_result(self) -> MyAccumulatedResult; 116 | } 117 | 118 | fn example_dispatcher() -> String { 119 | let mut dispatcher = MyAccumulatingDispatcher::new(); 120 | dispatch_things(&mut dispatcher); 121 | dispatcher.into_result() 122 | } 123 | 124 | async fn dispatch_things(context: Context, dispatcher: &mut dyn DispatchItem) { 125 | for item in context.items() { 126 | dispatcher.dispatch_item(item).await; 127 | } 128 | } 129 | ``` 130 | 131 | She uses the `dyner::preallocate::create_struct` macro to create a `PreDispatchItem` struct she can use for dynamic dispatch: 132 | 133 | ```rust 134 | #[dyner::preallocate::for_trait(PreDispatchItem)] 135 | trait DispatchItem { 136 | async fn dispatch_item(&mut self) -> Result<(), DispatchError>; 137 | } 138 | ``` 139 | 140 | Now she is able to use the same pattern to call `dispatch_things`. This time she wraps an `&mut dispatcher` instead of taking ownership of `dispatcher`. That works just fine since the trait only has an `&mut self` method. This way she can still call `into_result`: 141 | 142 | ```rust 143 | fn example_dispatcher() -> MyAccumulatedResult { 144 | let mut dispatcher = MyDispatcher::new(); 145 | let mut dispatcher = PreDispatchItem::new(&mut dispatcher); 146 | dispatch_things(&mut dispatcher); 147 | dispatcher.into_result() 148 | } 149 | ``` 150 | 151 | ### Other strategies 152 | 153 | Reading the docs, Grace finds a few other strategies available for dynamic dispatch. They all work the same way: a procedural macro generates a custom wrapper type for the trait that handles custom dispatch cases. Some examples: 154 | 155 | * Choosing from one of a fixed number of alternatives; returning an enum as the future and not a `Box`. 156 | -------------------------------------------------------------------------------- /roadmap/async_drop.md: -------------------------------------------------------------------------------- 1 | # Async drop 2 | 3 | ## Impact 4 | 5 | * Able to create types (database connections etc) that perform async operations on cleanup 6 | * Able to detect when such types are dropped synchronously 7 | * Able to identify the await points that result from async cleanup if needed 8 | 9 | ## Design notes 10 | 11 | We can create a `AsyncDrop` variant that contains an `async fn`: 12 | 13 | ```rust 14 | impl AsyncDrop for MyType { 15 | async fn drop(&mut self) { 16 | ... 17 | } 18 | } 19 | ``` 20 | 21 | Like `Drop`, the `AsyncDrop` trait must be implemented for all values of its self-type. 22 | 23 | ### Async drop glue 24 | 25 | Within async functions, when we drop a value, we will invoke "async drop glue" instead of "drop glue". "Async drop glue" works in the same basic way as "drop glue", except that it invokes `AsyncDrop` where appropriate (and may suspend): 26 | 27 | * The async drop glue for a type T first executes the `AsyncDrop` method 28 | * If `T` has no `AsyncDrop` impl, then the glue executes the synchronous `Drop` impl 29 | * If `T` has no `Drop` impl, then this is a no-op 30 | * The async drop glue then recursively "async drops" all fields of T 31 | 32 | ### Auto traits 33 | 34 | Rust presently assumes *all* types are droppable. Consider a function `foo`: 35 | 36 | ```rust 37 | async fn foo(x: T) {} 38 | ``` 39 | 40 | Here, we will drop `x` when `foo` returns, but we do not know whether `T` implements `AsyncDrop` or not, and we won't know until monomorphization. However, to know whether the resulting future for `foo(x)` is `Send`, we have to know whether the code that drops `x` will be send. So we must come up with a way to know that `T: Send` implies that the async drop future for `T` is `Send`. 41 | 42 | ### Explicit async drop 43 | 44 | We should have a `std::mem::async_drop` analogous to `std::mem::drop`: 45 | 46 | ```rust 47 | async fn async_drop(x: T) { } 48 | ``` 49 | 50 | ### Implicit await points 51 | 52 | When you run async drop glue, there is an implicit await point. Consider this example: 53 | 54 | ```rust 55 | async fn foo(dbc: DatabaseConnection) -> io::Result<()> { 56 | let data = socket().read().await?; 57 | dbc.write(data).await?; 58 | } 59 | ``` 60 | 61 | Here, presuming that `DatabaseConnection` implements `AsyncDrop`, there are actually a number of async drops occurring: 62 | 63 | ```rust 64 | async fn foo(dbc: DatabaseConnection) -> io::Result<()> { 65 | let data = match socket().read().await { 66 | Ok(v) => v, 67 | Err(e) => { 68 | std::mem::async_drop(dbc).await; 69 | return e; 70 | } 71 | }; 72 | let () = match dbc.write(data).await? { 73 | Ok(()) => (), 74 | Err(e) => { 75 | std::mem::async_drop(dbc).await; 76 | return e; 77 | } 78 | }; 79 | std::mem::async_drop(dbc).await; 80 | } 81 | ``` 82 | 83 | As this example shows, there are important ergonomic benefits here to implicit async drop, and it also ensures that async and sync code work in analogous ways. However, implicit await points can be a hazard for some applications, where it is important to identify all await points explicitly (for example, authors of embedded applications use await points to reason about what values will be stored in the resulting future vs the stack of the poll function). To further complicate things, async-drop doesn't only execute at the end of a block or an "abrupt" expression like `?`: async-drop can also execute at the end of every statement, given temporary values. 84 | 85 | The best solution here is unclear. We could have an "allow-by-default" lint encouraging explicit use of `async_drop`, but as the code above shows, the result may be highly unergonomic (also, imagine how it looks as the number of variables requiring async-drop grows). 86 | 87 | Another option is to target the problem from another angle, for example by adding lints to identify when large values are stored in a future or on the stack, or to allow developers to tag local variables that they expect to be stored on the stack, and have the compiler warn them if this turns out to not be true. Users could then choose how to resolve the problem (for example, by shortening the lifetime of the value so that it is not live across an await). 88 | 89 | #### Running destructors concurrently 90 | 91 | It's often the case that at the end of a function or scope, multiple destructors are run. In general the order (which is the reverse order of initialization) matters, since one local could borrow from another, or there is some other logical dependency between them. 92 | 93 | However, in some cases the order might not matter at all. In async, it would be possible to run destructors for multiple locals concurrently. As an example, we could mark the destructors like this: 94 | 95 | ```rust 96 | #[concurrent] 97 | impl AsyncDrop for Foo { 98 | async fn drop(&mut self) { ... } 99 | } 100 | ``` 101 | 102 | Here, `#[concurrent]` means that `Foo` does not take _logical_ dependencies or dependents with other values, and it is safe to drop concurrently. (The compiler would still enforce memory safety, of course.) 103 | 104 | In these cases, however, it's usually enough to impl _synchronous_ Drop and spawn a task for the "real" destructor. That keeps the language simple, though it's less convenient to write. 105 | 106 | ### Preventing sync drop 107 | 108 | It is easy enough to make async-drop be used, but it is currently not possible to prevent sync drop, even from within an async setting. Consider an example such as the following: 109 | 110 | ```rust 111 | async fn foo(dbc: DatabaseConnection) -> io::Result<()> { 112 | drop(dbc); 113 | } 114 | ``` 115 | 116 | The compiler could however lint against invoking (or defining!) synchronous functions that take ownership of values whose types implement `AsyncDrop`. This would catch code like the case above. We may have to tune the lint to avoid false warnings. Note that it is important to lint *both* invocation *and* definition sites because the synchronous function may be generic (like `drop`, in fact). 117 | 118 | The question remains: what should code that implements `AsyncDrop` *do* if synchronous `Drop` is invoked? One option is panic, but that is suboptimal, as panic from within a destructor is considered bad practice. Another option is to simply abort. A final option is to have some form of portable "block-on" that would work, but this is effectively the (as yet unsolved) [async-sync-async sandwich problem](https://rust-lang.github.io/wg-async-foundations/vision/shiny_future/users_manual.html#bridging-the-sync-and-async-worlds). 119 | 120 | Preventing this 'properly' would require changing fundamental Rust assumptions (e.g., by introducing the `?Drop` trait). While such a change would make Rust more expressive, it also carries complexity and composition hazards, and would require thorough exploration. It is also a step that could be taken later (although it would require some form of explicit `impl !Drop` opt-in by types to avoid semver breakage). 121 | 122 | ### Supporting both sync and async drop 123 | 124 | It should perhaps be possible to support *both* sync and async drop. It is not clear though if there are any real use cases for this. -------------------------------------------------------------------------------- /evaluation/design/dynx/sealed_traits.md: -------------------------------------------------------------------------------- 1 | # Dynx and sealed traits 2 | 3 | As described here, every dyn-safe trait `Trait` gets an "accompanying" `dynx Trait` struct and an `impl Trait for dynx Trait` impl for that struct. This can have some surprising interactions with unsafe code -- if you have a trait that can only be safely implemented by types that meet certain criteria, the impl for a `dynx` type may not meet those criteria. This can lead to undefined behavior. The question then is: *whose fault is that?* In other words, is it the language's fault, for adding impls you didn't expect, or the code author's fault, for not realizing those impls would be there (or perhaps for not declaring that their trait had additional safety requirements, e.g. by making the trait unsafe). 4 | 5 | A `dynx Trait` type is basically an implicit struct that implements `Trait`. For some traits, notably "sealed" traits, this could potentially be surprising. 6 | 7 | Consider a trait `PodTrait` that is meant to be implemented only for "plain old data" types, like `u8` and `u32`. By leveraging the "sealed trait" pattern, one can make it so that users cannot provide any impls of this trait: 8 | 9 | ```rust 10 | pub mod public_api { 11 | mod sealed { 12 | pub trait Supertrait { } 13 | } 14 | 15 | /// **Guarantee.** Every type `T` that implements `PodTrait` meets 16 | /// this condition: 17 | /// 18 | /// * Given a `&T`, there are `byte_len` bytes of data behind the 19 | /// pointer that safely be zeroed 20 | pub trait PodTrait: sealed::Supertrait { 21 | fn byte_len(&self) -> usize; 22 | } 23 | impl PodTrait for u8 { 24 | fn byte_len(&self) -> usize { 1 } 25 | } 26 | impl PodTrait for u32 { 27 | fn byte_len(&self) -> usize { 4 } 28 | } 29 | ... 30 | } 31 | ``` 32 | 33 | This trait could then be used to build safe abstractions that leverage unsafe reasoning: 34 | 35 | ```rust 36 | pub mod public_api { 37 | ... 38 | 39 | pub fn zero(pod: &mut impl PodTrait) { 40 | let n = pod.byte_len(); 41 | 42 | // OK: We have inspected every impl of `PodTrait` 43 | // and we know that `byte_len` accurately 44 | // describes the length of a pointer. 45 | unsafe { 46 | <*mut _>::write_bytes(pod, 0, n) 47 | } 48 | } 49 | 50 | ... 51 | } 52 | ``` 53 | 54 | Unfortunately, this reasoning is not sound if combined with a `dynx` type: 55 | 56 | ```rust 57 | pub mod public_api { 58 | ... 59 | 60 | trait GetPodTrait { 61 | fn get(&self) -> impl PodTrait; 62 | } 63 | } 64 | 65 | fn method(x: &dyn GetPodTrait) { 66 | // `y` will be a `dynx GetPodTrait`, which will be 67 | // a `Box` or `Box` 68 | let mut y = x.get(); 69 | 70 | // Calling `zero` is trying to zero the memory *referenced* by 71 | // the box, but it will actually zero the box pointer itself. 72 | // Bad! 73 | public_api::zero(&mut y); 74 | } 75 | ``` 76 | 77 | What went wrong here? The problem is implementing `PodTrait` carries an additional proof obligation beyond those that the Rust type checker is aware of. As the comment says, one must guarantee that `byte_len`, invoked on an `&impl PodTrait`, correctly describes the number of bytes in that memory (and that it can safely be zeroed). This invariant was manually verified for the `u8` and `u32` impls, but it does not hold for the `impl PodTrait for dynx PodTrait` generated by the compiler. 78 | 79 | There are a few ways to resolve this conundrum: 80 | 81 | * Declare that `PodTrait` should have been declared as `unsafe`, and that `-> impl Foo` is not dyn safe if `Foo` is an unsafe trait. 82 | * One could argue that the original code was wrong to declare `PodTrait` as safe; if it were declared as `unsafe`, and we used that to suppress the `dynx` impl (after all, we can't know whether it satisfies the extra conditions), the above code would no longer compile. 83 | * However, many Rust devs consider `unafe` to be more of a tool for the user -- if you have private functions and types, you are supposed to be able to reason fully about what they do without needing internal `unsafe` annotations (there hasn't been a formal decision about whether this is a valid principle, but it has many adherents, and it's possible we should make it one). 84 | * Furthermore, this means that unsafe traits can't be used with `dynx`, which could *in some cases* be useful. 85 | * We could have an opt-in for this case. 86 | * Some form of opt-in for dyn compatibility (aka, dyn safety). 87 | * Ignoring backwards compatiblility, we could require traits to "opt-in" to being dyn-compatible (e.g., one might write `dyn trait Foo` instead of just having a `trait Foo` that doesn't make use of various prohibited features). In that case, the user is "opting in" to the generation of the `dynx` impl; e.g., a `dyn trait PodTrait` declaration would be simply broken, because that `dyn trait` declaration implies the generation of an `impl PodTrait for dynx PodTrait`, and the safety criteria are not met (this is subtle, but then the code itself is subtle). 88 | * We could leverage editions in various ways to resolve the backwards compatibility issues. 89 | * Arguments in favor of opt-in: 90 | * The fact that the compiler implicitly decides whether your trait is dyn compatible or not is a common source of user confusion -- it's easy to not realize you are promising people dyn compatibility, and also easy to not realize that you've lost it via some change to the crate. 91 | * Furthermore, there are some soundness holes (e.g., [#57893](https://github.com/rust-lang/rust/issues/57893)) that are much harder to fix because there is no declaration of dyn compatibility. In those cases, if we knew the trait was meant to be dyn compatible, we could easily enforce stricter rules that would prevent unsoundness. But detecting whether the trait would meet those stricter rules is difficult, and so it is hard to determine automatically whether the trait should be dyn compatible or not. 92 | * Finally, some form of explicit "opt-in" to dyn compatibility might allow us to close various ergonomic holes, such as automatically providing an impl of `Trait` for `Box`, `&dyn Trait`, and other pointer types (perhaps all pointer types). 93 | * Arguments against: 94 | * Backwards compatibility. We currently determine automatically if traits are "dyn compatible" or not. That code has to keep working. 95 | * Arguably, traits should be dyn compatible by default, and you should "opt out" to make use of the extended set of features that purely static traits provide (we could, of course, do that over an edition). 96 | * If a trait `SomeTrait` includes an `-> impl Foo` method, make the trait dyn safe only if `SomeTrait` could *itself* have declared a struct that implements `Foo` 97 | * This is a rather wacky rule, but the idea is that if something is "sealed", then either (a) the trait `SomeTrait` is outside the sealed abstraction, and so won't be able to implement it; or (b) it's your own fault, because it's inside the sealed abstraction. 98 | * In this case, the offending code is inside the sealed abstraction, so we'd still have the bug. 99 | * Introduce a "sealed" keyword and declare that code is wrong. 100 | * We would rather not entangle these designs; also, like the "unsafe"-based solution, combining sealed + dyn compatible would likely make sense. 101 | -------------------------------------------------------------------------------- /explainer/async_fn_in_dyn_trait/nested_impl_trait.md: -------------------------------------------------------------------------------- 1 | # Nested `impl Trait` and dyn adaptation 2 | 3 | We've seen that `async fn` desugars to a regular function returning `impl Future`. But what happens when we have *another* `impl Trait` inside the return value of the `async fn`? 4 | 5 | ```rust 6 | trait DebugStream { 7 | async fn next(&mut self) -> Option; 8 | } 9 | 10 | impl DebugStream for Factory { 11 | async fn next(&mut self) -> Option { 12 | if self.done { 13 | None 14 | } else { 15 | Some(&self.debug_state) 16 | } 17 | } 18 | } 19 | ``` 20 | 21 | ## Trait desugaring 22 | 23 | How does something like this desugar? Let's start with the basics... 24 | 25 | The trait first desugars to: 26 | 27 | ```rust 28 | trait DebugStream { 29 | fn next(&mut self) -> 30 | impl Future + '_; 31 | } 32 | ``` 33 | 34 | which further desugars to: 35 | 36 | ```rust 37 | trait DebugStream { 38 | type next<'me>: Future 39 | // ^^^^^ 40 | // This lifetime wouldn't be here if not for 41 | // the `'_` in `impl Debug + '_` 42 | where 43 | Self: 'me; 44 | fn next(&mut self) -> Self::next<'_>; 45 | } 46 | ``` 47 | 48 | which further desugars to: 49 | 50 | ```rust 51 | trait DebugStream { 52 | type next<'me>: Future> 53 | where 54 | Self: 'me; 55 | type next_0<'a>: Debug 56 | // ^^^^ 57 | // This lifetime wouldn't be here if not for 58 | // the `'_` in `impl Debug + '_` 59 | where 60 | Self: 'a; // TODO is this correct? 61 | fn next(&mut self) -> Self::next<'_>; 62 | } 63 | ``` 64 | 65 | As we can see, this problem is more general than `async fn`. We'd like a solution to work for any case of nested `impl Trait`, including on associated types. 66 | 67 | ## Impl desugaring 68 | 69 | Pretty much the same as the trait. 70 | 71 | The impl desugars to: 72 | 73 | ```rust 74 | impl DebugStream for Factory { 75 | fn next(&mut self) -> 76 | impl Future> + '_ 77 | {...} 78 | } 79 | ``` 80 | 81 | which further desugars to: 82 | 83 | ```rust 84 | impl DebugStream for Factory { 85 | type next<'me> = 86 | impl Future> + 'me 87 | // ^^^^^ 88 | // This lifetime wouldn't be here if not for 89 | // the `'_` in `impl Debug + '_` 90 | where 91 | Self: 'me; // TODO is this correct? 92 | fn next(&mut self) -> Self::next<'me> 93 | {...} 94 | } 95 | ``` 96 | 97 | which further desugars to: 98 | 99 | ```rust 100 | impl DebugStream for Factory { 101 | type next<'me> = 102 | impl Future>> + 'me 103 | where 104 | Self: 'me; 105 | type next_0<'a> = impl Debug + 'a 106 | // ^^^^ 107 | // This lifetime wouldn't be here if not for 108 | // the `'_` in `impl Debug + '_` 109 | where 110 | Self: 'a; 111 | fn next(&mut self) -> Self::next<'me> 112 | {...} 113 | } 114 | ``` 115 | 116 | ## Dyn adaptation 117 | 118 | Let's start by revisiting out the "basic" case: returning regular old `impl Future`. 119 | 120 | ```rust 121 | trait BasicStream { 122 | async fn next(&mut self) -> Option; 123 | // Desugars to: 124 | fn next(&mut self) -> impl Future>; 125 | } 126 | 127 | struct Counter(i32); 128 | impl BasicStream for Counter { 129 | async fn next(&mut self) -> Option {...} 130 | } 131 | ``` 132 | 133 | As we saw before, the compiler generates a shim for our type's `next` function: 134 | 135 | ```rust 136 | // Pseudocode for the compiler-generated shim that goes in the vtable. 137 | fn counter_next_shim( 138 | this: &mut Counter, 139 | ) -> dynx Future> { 140 | // We would skip boxing for #[dyn(identity)] 141 | let boxed = Box::pin(::next(this)); 142 | ::new(boxed) 143 | } 144 | ``` 145 | 146 | Now let's attempt to do the same thing for our original example. Here it is from above: 147 | 148 | ```rust 149 | trait DebugStream { 150 | async fn next(&mut self) -> Option; 151 | } 152 | 153 | impl DebugStream for Factory { 154 | fn next(&mut self) -> 155 | impl Future> + '_ 156 | {...} 157 | } 158 | ``` 159 | 160 | Generating the shim here is more complicated, because now it must do two layers of wrapping. 161 | 162 | ```rust 163 | // Pseudocode for the compiler-generated shim that goes in the vtable. 164 | fn factory_next_shim( 165 | this: &mut Counter, 166 | ) -> dynx Future> { 167 | let fut: impl Future = 168 | ::next(this); 169 | 170 | // We need to turn the above fut into: 171 | // impl Future 172 | // To do this, we need *another* shim... 173 | struct FactoryNextShim<'a>(Factory::next<'a>); 174 | impl<'a> Future for FactoryNextShim<'a> { 175 | type Output = Option; 176 | fn next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 177 | let ret = as Future>::poll( 178 | // This is always sound, probably 179 | pin_project!(self).0, 180 | cx, 181 | ); 182 | match ret { 183 | Poll::Ready(Some(output)) => { 184 | // We would skip boxing for #[dyn(identity)] on 185 | // impl Future for Factory::next.. which means 186 | // #[dyn(identity)] on the impl async fn? 187 | // Or do we provide a way to annotate the 188 | // future and `impl Debug` separately? TODO 189 | // 190 | // Why Box::new and not Box::pin like below? 191 | // Because `Debug` has no `self: Pin` methods. 192 | let boxed = Box::new(output); 193 | Poll::Ready(::new(boxed)) 194 | } 195 | Poll::Ready(None) | Poll::Pending => { 196 | // No occurrences of `Output` in these variants. 197 | Poll::Pending 198 | } 199 | } 200 | } 201 | } 202 | let wrapped = FactoryNextShim(fut); 203 | 204 | // We would skip boxing for #[dyn(identity)] 205 | // Why Box::pin? Because `Future` has a `self: Pin` method. 206 | let boxed = Box::pin(wrapped); 207 | ::new(boxed) 208 | } 209 | ``` 210 | 211 | This looks to be a lot of code, but here's what it boils down to: 212 | 213 | > For some `impl Foo`, 214 | > 215 | > We generate a wrapper type of our outer `impl Foo` and implement the `Foo` trait on it. Our implementation forwards the methods to the actual type, takes the return value, and maps any occurrence of the associated type `A` in the return type to `dynx Bar`. 216 | 217 | This mapping can be done structurally on the return type, and it benefits from all the flexibility of `dynx` that we saw before. That means it works for references to `A`, provided the lifetime bounds on the trait's `impl Bar` allow for this. 218 | 219 | There are probably cases that can't work. We should think more about what those are. 220 | -------------------------------------------------------------------------------- /evaluation/case-studies/builder-provider-api.md: -------------------------------------------------------------------------------- 1 | # Async Builder + Provider API Case Study 2 | 3 | This case study presents a common API pattern found in builders in the AWS SDK. 4 | 5 | ## Current API 6 | 7 | Several builders in the AWS SDK follow a "async provider" model, where the builder takes an implementation of a trait returning a future to customize behavior. 8 | 9 | ```rust= 10 | let credentials = DefaultCredentialsChain::builder() 11 | // Provide an `impl ProvideCredentials` 12 | .with_custom_credential_source(MyCredentialsProvider) 13 | .build() 14 | .await; 15 | ``` 16 | 17 | In this example, the user is able to add a custom credentials source for a `DefaultCredentialsChain`. This credentials source is allowed to do async work upon invocation by the credentials chain. The `with_custom_credential_source` builder method takes an implementation of the `ProvideCredentials` trait: 18 | 19 | ```rust= 20 | pub trait ProvideCredentials: Send + Sync + Debug { 21 | fn provide_credentials(&self) -> ProvideCredentials<'_>; 22 | } 23 | ``` 24 | 25 | The current `ProvideCredentials` trait is a bit awkward. It expects the implementor to return an instance of [`ProvideCredentials<'_>`](https://docs.rs/aws-credential-types/0.54.1/aws_credential_types/provider/future/struct.ProvideCredentials.html) struct, which acts like a boxed future that yields `Result`: 26 | 27 | ```rust= 28 | struct MyCredentialsProvider; 29 | // Implementations return `ProvideCredentials<'_>`, which is basically a boxed 30 | // `impl Future>`. 31 | impl ProvideCredentials for MyCredentialsProvider { 32 | fn provide_credentials(&self) -> ProvideCredentials<'_> { 33 | ProvideCredentials::new(async move { 34 | /* Make some credentials */ 35 | }) 36 | } 37 | } 38 | ``` 39 | 40 | Under the hood, when the builder's `with_custom_credential_source` is called, it boxes the `impl ProvideCredentials` and stores it for use in the `DefaultCredentialsChain` that will be built. 41 | 42 | ## With AFIT 43 | 44 | Since `ProvideCredentials` basically returns an `impl Future` already, with AFIT, `ProvideCredentials` can instead be simplified to: 45 | 46 | ```rust= 47 | trait ProvideCredentials { 48 | async fn provide_credentials(&self) -> Result; 49 | } 50 | ``` 51 | 52 | The user can then provide an implementation of the trait without the extra step of wrapping the function body in `ProvideCredentials::new(async { ... })`. 53 | 54 | ```rust= 55 | impl ProvideCredentials for MyCredentialsProvider { 56 | async fn provide_credentials(&self) -> Result { 57 | let credentials = query_something().await?; 58 | // do other things like validation 59 | Ok(credentials) 60 | } 61 | } 62 | ``` 63 | 64 | And the builder invocation remains the same... 65 | ```rust= 66 | let credentials = DefaultCredentialsChain::builder() 67 | // Provide an `impl ProvideCredentials` 68 | .with_custom_credential_source(MyCredentialsProvider) 69 | .build() 70 | .await; 71 | ``` 72 | 73 | ### Dynamic Dispatch: Behind the API 74 | 75 | To make this change to the builder, we need to take instances of the new `ProvideCredentials` trait. Without AFIDT[^1], we can't simply box the `impl ProvideCredentials` in `with_custom_credential_source` like we were doing before. 76 | 77 | Luckily, we can use a small type erasure hack to get around the lack of AFIDT, introducing a new trait called `ProvideCredentialsDyn` that has a blanket impl for all implementors of `ProvideCredentials`: 78 | 79 | [^1]: "Async functions in dyn trait", allowing traits with `async fn` methods to be object safe. 80 | 81 | ```rust= 82 | trait ProvideCredentialsDyn { 83 | fn provide_credentials(&self) -> Pin> + '_>>; 84 | } 85 | 86 | impl ProvideCredentialsDyn for T { 87 | fn provide_credentials(&self) -> Pin> + '_>> { 88 | Box::pin(::provide_credentials(self)) 89 | } 90 | } 91 | ``` 92 | 93 | This new `ProvideCredentialsDyn` trait *is* object-safe, and can be boxed and stored inside the builder instead of `ProvideCredentials`: 94 | 95 | ```rust= 96 | struct DefaultCredentialsChain { 97 | credentials_source: Box, 98 | // ... 99 | } 100 | 101 | impl DefaultCredentialsChain { 102 | fn with_custom_credential_source(self, provider: impl ProvideCredentials) { 103 | // Coerce `impl ProvideCredentials` to `Box` 104 | Self { provider: Box::new(credentials_source), ..self } 105 | } 106 | } 107 | ``` 108 | 109 | This extra trait is an implementation detail that is not in the public-facing, API so it can be migrated away when support for AFIDT is introduced. 110 | 111 | A full builder pattern example is implemented here: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=8daf7b2d5236e581f78d2c09310d09ac 112 | 113 | ## Send bounds 114 | 115 | One limitation with the proposed async version of `ProvideCredentials` is the lack of a `Send` bound on the future returned by `ProvideCredentials`. This bound is enforced by the pre-AFIT version of this trait, so any futures using the builder will not be `Send` after AFIT migration. 116 | 117 | To fix this, we could use a return type bound[^2] on the `with_custom_credential_source` builder method: 118 | 119 | [^2]: https://smallcultfollowing.com/babysteps/blog/2023/02/13/return-type-notation-send-bounds-part-2/ 120 | 121 | ```rust 122 | impl DefaultCredentialsChain { 123 | fn with_custom_credential_source( 124 | self, 125 | provider: impl ProvideCredentials 126 | ) { 127 | // Coerce `impl ProvideCredentials` to `Box` 128 | Self { provider: Box::new(credentials_source), ..self } 129 | } 130 | } 131 | ``` 132 | 133 | Then the `ProvideCredentialsDyn` trait could be modified to return `Pin> + Send + '_>>`: 134 | 135 | ```rust= 136 | trait ProvideCredentialsDyn { 137 | fn provide_credentials(&self) -> Pin> + Send + '_>>; 138 | } 139 | 140 | impl> ProvideCredentialsDyn for T { 141 | fn provide_credentials(&self) -> Pin> + Send + '_>> { 142 | Box::pin(::provide_credentials(self)) 143 | } 144 | } 145 | ``` 146 | 147 | Alternative and equivalent to this would be something like bounding by `T: async(Send) ProvideCredentials`, which may look like: 148 | 149 | ```rust= 150 | impl ProvideCredentialsDyn for T { 151 | fn provide_credentials(&self) -> Pin> + Send + '_>> { 152 | Box::pin(::provide_credentials(self)) 153 | } 154 | } 155 | ``` 156 | 157 | ## Usages 158 | 159 | The SDK uses this same idiom several times: 160 | * `ProvideCredentials`: https://docs.rs/aws-credential-types/0.54.1/aws_credential_types/provider/trait.ProvideCredentials.html 161 | * `AsyncSleep`: https://docs.rs/aws-smithy-async/0.54.3/aws_smithy_async/rt/sleep/trait.AsyncSleep.html 162 | * `ProvideRegion`: https://docs.rs/aws-config/0.54.1/aws_config/meta/region/trait.ProvideRegion.html 163 | 164 | ## Future improvements 165 | 166 | With AFIDT, we can drop the `ProvideCredentialsDyn` trait and just use `Box` as is. Refactoring the API to use AFIDT is a totally internal-facing change. -------------------------------------------------------------------------------- /evaluation/case-studies/socket-handler.md: -------------------------------------------------------------------------------- 1 | # Netstack3 Async Socket Handler Case Study 2 | 3 | This case study presents a simplification of a common handler pattern enabled by `async_fn_in_trait` (AFIT). 4 | 5 | ## Background 6 | 7 | [Netstack3] is a networking stack written in Rust for the [Fuchsia operating 8 | system][Fuchsia]. As a Fuchsia component, Netstack3 communicates with its 9 | clients using Fuchsia-native asynchronous IPC via generated bindings from 10 | Fuchsia interface definition language ([FIDL]) specifications. Netstack3 11 | provides, via various FIDL protocols, the ability for other Fuchsia components 12 | (e.g. applications) to create and manipulate POSIX-like socket objects. 13 | 14 | ## Per-socket handler implementations 15 | 16 | To allocate a socket, a Fuchsia component sends a single message to the netstack 17 | indicating the desired type of socket, along with a [Zircon channel] that the 18 | netstack is expected to listen for requests on. The other end of the channel is 19 | held by the client and used to send requests for the new socket to the netstack. 20 | 21 | When Netstack3 receives a request to create a socket, it spawns a new 22 | [`fuchsia_async::Task`] to dispatch incoming requests for the socket to the 23 | socket's handler. The type of messages for the socket depends on the protocol, 24 | so Netstack3 has distinct handler types for each. 25 | 26 | Though the handler types are distinct, their functionality is fairly similar: 27 | 28 | - Handlers wait to receive incoming requests on their channel, then process 29 | them. 30 | - Handlers process each received message completely before polling for the next. 31 | - Handlers support `Clone` requests by merging a new stream of incoming 32 | requests with their existing one. 33 | - Handlers exit when either their request stream ends or in response to an 34 | explicit `Close` message. 35 | - On exit, handlers clean up any resources corresponding to the socket. 36 | 37 | Request handling for a given socket is done using `async`/`await`. Though each 38 | individual socket handles requests serially, this allows requests for different 39 | sockets to be handled concurrently by the executor. 40 | 41 | ## Individual implementations 42 | 43 | Before the introduction of `async_fn_in_trait`, Netstack3's socket handlers were 44 | each implemented independently, with minimal code reuse. This resulted in 45 | significant duplication of code for the common behaviors above. The 46 | straightforward refactor would define a generic `SocketWorker` type with 47 | the shared behavior, and that would delegate to an implementer of a trait 48 | `SocketHandler` for socket-type-specific request handling: 49 | 50 | ```rust 51 | pub struct SocketWorker { 52 | handler: H, 53 | } 54 | 55 | pub trait SocketHandler: Default { 56 | type Request; 57 | 58 | /// Handles a single request. 59 | async fn handle_request( 60 | &mut self, 61 | request: Self::Request, 62 | ) -> ControlFlow<(), Option>; 63 | 64 | /// Closes the socket managed by this handler. 65 | fn close(self); 66 | } 67 | 68 | impl SocketWorker { 69 | /// Starts servicing events from the provided event stream. 70 | pub async fn serve_stream(stream: RequestStream) { 71 | Self { handler: H::default() }.handle_stream(stream) 72 | } 73 | 74 | async fn handle_stream(mut self, requests: RequestStream) { 75 | let Self {handler} = self; 76 | while let Some(request) = requests.next().await { 77 | // Call `handler.handle_request()` for each request while merging 78 | // new request streams into `requests`. 79 | } 80 | handler.close() 81 | } 82 | } 83 | ``` 84 | 85 | Because `SocketHandler::handle_request` is an `async fn`, this won't compile on 86 | the current version of stable Rust (1.68.0 as of writing). There are a 87 | [couple options][AFIT workarounds] for working around the lack of support for 88 | AFIT, but they each have significant downsides: 89 | 90 | ### Option 1: Make request handling a hand-rolled `Future` impl on a custom type 91 | 92 | One way to work around the lack of AFIT is to declare a non-`async` trait 93 | function that returns an instance of an associated type that implements 94 | `Future`. This works, but requires explicitly structuring control flow as state 95 | within the associated type. For Netstack3, where request handling branches down 96 | tens of paths, maintaining this state machine by hand would be impractically 97 | difficult. 98 | 99 | ### Option 2: Use dynamic dispatch 100 | 101 | This is similar to the above, but instead of an associated type, the non-`async` 102 | function returns a `Box`. The trait implementation can then call an 103 | `async fn`, then box up and return the result. This results in more readable 104 | code than option 1 at the cost of allocation and dynamic dispatch at run time. 105 | For Netstack3, which is on the critical path of every network-connected 106 | component in the system, this is not worth the benefits of abstraction. 107 | 108 | ## With AFIT 109 | 110 | Using `async_fn_in_trait` allowed performing the refactoring proposed above 111 | without workarounds. The resulting code doesn't use dynamic dispatch, and the 112 | implementations of `SocketHandler::handle_request` are written as 113 | regular `async fn`s. The full definition of the abstract worker and handler 114 | trait can be found in the [Netstack3 source code][socket worker source]). 115 | 116 | ## `Send` bound limitation 117 | 118 | One of the current limitations for AFIT is the 119 | [inability to specify bounds][AFIT spawn from generic] on the type of the future 120 | returned from an async trait function. This can cause errors when the caller of 121 | the function requires a `Send` bound so that the future can be passed to a 122 | multi-threaded executor. 123 | 124 | Netstack3 uses [`fuchsia_async::Task::spawn`] to create tasks that can run on 125 | Fuchsia's multi-threaded executor, and so initial attempts to use AFIT ran afoul 126 | of the limitation. Luckily, the suggested workaround of moving the spawn point 127 | out of generic code worked for Netstack3: `Task::spawn` is [called in 128 | socket-specific code][datagram diff] instead of within generic socket worker 129 | code. Since the compiler has access to the concrete `Future`-implementing type 130 | returned by the specific impl of `SocketHandler::handle_request`, it can verify 131 | that it and all its callers implement `Send`. 132 | 133 | ## Future usages 134 | 135 | While AFIT is currently being used in Netstack3 for abstracting over socket 136 | behaviors, it's likely that there are other places where it would prove useful, 137 | including in some of the existing per-IP-version code with common logic. 138 | 139 | [Netstack3]: https://fuchsia.dev/fuchsia-src/contribute/roadmap/2021/netstack3 140 | [Fuchsia]: https://fuchsia.dev/ 141 | [FIDL]: https://fuchsia.dev/fuchsia-src/development/languages/fidl 142 | [Zircon channel]: https://fuchsia.dev/fuchsia-src/reference/kernel_objects/channel?hl=en 143 | [`fuchsia_async::Task`]: https://fuchsia-docs.firebaseapp.com/rust/fuchsia_async/struct.Task.html 144 | [AFIT workarounds]: https://blog.rust-lang.org/inside-rust/2022/11/17/async-fn-in-trait-nightly.html#workarounds-available-in-the-stable-compiler 145 | [socket worker source]: https://cs.opensource.google/fuchsia/fuchsia/+/d38d260043834b14b5dbf8e315ef11c712f12166:src/connectivity/network/netstack3/src/bindings/socket/worker.rs 146 | [AFIT spawn from generic]: https://blog.rust-lang.org/inside-rust/2022/11/17/async-fn-in-trait-nightly.html#limitation-spawning-from-generics 147 | [`fuchsia_async::Task::spawn`]: https://fuchsia-docs.firebaseapp.com/rust/fuchsia_async/struct.Task.html#method.spawn 148 | [datagram diff]: https://fuchsia.googlesource.com/fuchsia/+/95808a53ec3507aeaa2b185363b45b72553e1d5e%5E%21/#F1 -------------------------------------------------------------------------------- /evaluation/case-studies/microsoft.md: -------------------------------------------------------------------------------- 1 | # Microsoft Async Case Study 2 | 3 | ## Background 4 | 5 | Microsoft uses async Rust in several projects both internally and externally. In 6 | this case study, we will focus on how async is used in one project in 7 | particular. 8 | 9 | This project manages and interacts with low level hardware resources. 10 | Performance and resource efficiency is key. Async Rust has proven useful not 11 | just because of it enables scalability and efficient use of resources, but also 12 | because features such as cancel-on-drop semantics simplify the interaction 13 | between components. 14 | 15 | Due to our constrainted, low-level environment, we use a custom executor but 16 | rely heavily on ecosystem crates to reduce the amount of custom code needed in 17 | our executor. 18 | 19 | ## Async Trait Usage 20 | 21 | The project makes regular use of async traits. Since these are not yet supported 22 | by the language, we have instead used the [`async-trait`] crate. 23 | 24 | For the most part this works well, but sometimes the overhead introduced by 25 | boxing the returned fututures is unacceptable. For those cases, we use 26 | [StackFuture], which allows us to emulate a `dyn Future` while storing it in 27 | space provided by the caller. 28 | 29 | Now that there is built in support for async in traits in the nightly compiler, 30 | we have tried porting some of our async traits away from the [`async-trait`] 31 | crate. 32 | 33 | For many of these the transformation was simple. We merely had to remove the 34 | `#[async_trait]` attribute on the trait and all of its implementations. For 35 | example, we had one trait that looks similar to this: 36 | 37 | ```rust 38 | #[async_trait] 39 | pub trait BusControl { 40 | async fn offer(&self) -> Result<()>; 41 | async fn revoke(&self) -> Result<()>; 42 | } 43 | ``` 44 | 45 | There were several implementations of this trait as well. In this case, all we 46 | needed to do was remove the `#[async_trait]` annotation. 47 | 48 | ### Send Bounds 49 | 50 | In about half the cases, we needed methods to return a future that was `Send`. 51 | This happens by default with `#[async_trait]`, but not when using the built-in 52 | feature. 53 | 54 | In these cases, we instead manually desugared the `async fn` definition so we 55 | could add additional bounds. Although these bounds applied at the trait 56 | definition site, and therefore to all implementors, we have not found this to be 57 | a deal breaker in practice. 58 | 59 | As an example, one trait that required a manual desugaring looked like this: 60 | 61 | ```rust 62 | pub trait Component: 'static + Send + Sync { 63 | async fn save<'a>( 64 | &'a mut self, 65 | writer: StateWriter<'a>, 66 | ) -> Result<(), SaveError>; 67 | 68 | async fn restore<'a>( 69 | &'a mut self, 70 | reader: StateReader<'a>, 71 | ) -> Result<(), RestoreError>; 72 | } 73 | ``` 74 | 75 | The desugared version looked like this: 76 | 77 | ```rust 78 | pub trait Component: 'static + Send + Sync { 79 | fn save<'a>( 80 | &'a mut self, 81 | writer: StateWriter<'a>, 82 | ) -> impl Future> + Send + 'a; 83 | 84 | fn restore<'a>( 85 | &'a mut self, 86 | reader: StateReader<'a>, 87 | ) -> impl Future> + Send + 'a; 88 | } 89 | ``` 90 | 91 | This also required a change to all the implementation sites since we were 92 | migrating from `async_trait`. This was slightly tedious but basically a 93 | mechanical change. 94 | 95 | ### Dyn Trait Workaround 96 | 97 | We use trait objects in several places to support heterogenous collections of 98 | data that implements a certain trait. Rust nightly does not currently have 99 | built-in support for this, so we needed to find a workaround. 100 | 101 | The workaround that we have used so far is to create a `DynTrait` version of 102 | each `Trait` that we need to use as a trait object. One example is the 103 | `Component` trait shown above. For the `Dyn` version, we basically just 104 | duplicate the definition but apply `#[async_trait]` to this one. Then we add a 105 | blanket implementation so that we can triviall get a `DynTrait` for any trait 106 | that has a `Trait` implementation. As an example: 107 | 108 | ```rust 109 | #[async_trait] 110 | pub trait DynComponent: 'static + Send + Sync { 111 | async fn save<'a>( 112 | &'a mut self, 113 | writer: StateWriter<'a>, 114 | ) -> Result<(), SaveError>; 115 | 116 | async fn restore<'a>( 117 | &'a mut self, 118 | reader: StateReader<'a>, 119 | ) -> Result<(), RestoreError>; 120 | } 121 | 122 | #[async_trait] 123 | impl DynComponent for T { 124 | async fn save(&mut self, writer: StateWriter<'_>) -> Result<(), SaveError> { 125 | ::save(self, writer).await 126 | } 127 | 128 | async fn restore( 129 | &mut self, 130 | reader: StateReader<'_>, 131 | ) -> Result<(), RestoreError> { 132 | ::restore(self, reader).await 133 | } 134 | } 135 | ``` 136 | 137 | It is a little annoying to have to duplicate the trait definition and write a 138 | blanket implementation, but there are some mitigating factors. First of all, 139 | this only needs to be done in once per trait and can conveniently be done next 140 | to the non-`Dyn` version of the trait. Outside of that crate or module, the user 141 | just has to remember to use `dyn DynTrait` instead of just `DynTrait`. 142 | 143 | The second mitigating factor is that this is a mechanical change that could 144 | easily be automated using a proc macro (although we have not done so). 145 | 146 | ### Return Type Notation 147 | 148 | Since there is an active PR implementing [Return Type Notation][RTN] (RTN), we 149 | gave that a try as well. The most obvious place this was applicable was on the 150 | `Component` trait we have already looked at. This turned out to be a little 151 | tricky. Because `async_trait` did not know how to parse RTN bounds, we had to 152 | forego the use of `#[async_trait]` and use a manually expanded version where 153 | each async function returned `Pin + Send + '_>>`. 154 | Once we did this, we needed to add `save(..): Send` and `restore(..): Send` 155 | bounds to the places where the `Component` trait was used as a `DynComponent`. 156 | There were only two methods we needed to bound, but this was still mildly 157 | annoying. 158 | 159 | The `#[async_trait]` limitation will likely go away almost immediately once the 160 | RTN PR merges, since it can be updated to support the new syntax. Still, this 161 | experience does highlight one thing, which is that the `DynTrait` workaround 162 | requires us to commit up front to whether that trait will guarantee `Send` 163 | futures or not. There does not seem to be an obvious way to push this decision 164 | to the use site like there is with Return Type Notation. 165 | 166 | This is something we will want to keep in mind with any macros we create to help 167 | automate these transformations as well as with built in language support for 168 | async functions in trait objects. 169 | 170 | [`async-trait`]: https://crates.io/crates/async-trait 171 | [StackFuture]: https://crates.io/crates/stackfuture 172 | [RTN]: https://github.com/rust-lang/rust/pull/109010 173 | 174 | ## Conclusion 175 | 176 | We have not seen any insurmountable problems with async functions in traits as 177 | they are currently implemented in the compiler. That said, it would be 178 | significantly more ergonomic with a few more improvements, such as: 179 | 180 | * A way to specify `Send` bounds without manually desugaring a function. Return 181 | type notation looks like it would work, but even in our limited experience 182 | with it we have run into ergonomics issues. 183 | * A way to simplify `dyn Trait` support. Language-provided support would be 184 | ideal, but a macro that automates something like our `DynTrait` workaround 185 | would be acceptable too. 186 | 187 | That said, we are eager to be able to use this feature in production! 188 | -------------------------------------------------------------------------------- /evaluation/case-studies/tower.md: -------------------------------------------------------------------------------- 1 | # Tower AFIT Case Study 2 | 3 | This case study presents how Tower would update it's core traits to improve 4 | ergonomics and useability with the aid of `async_fn_in_trait` (AFIT). 5 | 6 | ## Background 7 | 8 | [Tower] is a library of modular and reusable components for building robust 9 | networking clients and servers. The core use case of Tower is to enable users 10 | to compose "stacks" of middleware in an easy and reusable way. To achieve this 11 | currently Tower provide a [`Service`] and a [`Layer`] trait. The core 12 | component is the `Service` trait as this is where all the async functionality 13 | lives. `Layer`s on the other hand are service constructors and allow users to 14 | compose middleware stacks. Since, `Layer` does not do any async work it's trait 15 | design is out of scope of this document for now. 16 | 17 | Currently, the `Service` trait requires users to name their futures which does 18 | not allow them to easily use `async fn` without boxing. In addition, borrowing 19 | state within handlers is unergonomic due using associated types for the return 20 | future type. This is because to allow the returned future to have a lifetime 21 | it would require adding a lifetime generic associated type (GAT) which would force 22 | an already verbose trait into an extremely verbose trait that is unergonomic for 23 | users to implement. This thus requires all returned futures to be `'static` 24 | which means that it must own all of its data. While this works right now its an 25 | extra step that could be avoided by using `async fn`'s ability to produce 26 | non-static futures in an ergonomic way. 27 | 28 | The current `Service` trait looks something like this simplified version which 29 | omits `poll_ready`, `Response/Error` handling: 30 | 31 | ```rust 32 | trait Service { 33 | type Response; 34 | type Future: Future; 35 | 36 | fn call(&mut self, req: Request) -> Self::Future; 37 | } 38 | ``` 39 | 40 | Middleware are `Service`'s that wrap another `S: Service` such that it will 41 | internally call the inner service and wrap its behavior. This allows middleware 42 | to modify the input type, wrap the output future and modify the output type. 43 | 44 | Due to `Service::call` requiring `&mut self` some middleware require their inner 45 | `Service` to implement `Clone`. Unfortunetly, this bound has caused many issues 46 | as it makes layering services quite complex due to the nested inference that 47 | happens in [`ServiceBuilder`]. This has caused many massive and almost famous 48 | error messages produced from Tower. 49 | 50 | [Tower]: https://github.com/tower-rs/tower 51 | [`Service`]: https://docs.rs/tower/latest/tower/trait.Service.html 52 | [`ServiceBuilder`]: https://docs.rs/tower/latest/tower/struct.ServiceBuilder.html 53 | [`Layer`]: https://docs.rs/tower/latest/tower/trait.Layer.html 54 | 55 | ## Moving forward 56 | 57 | One of the goals of moving to AFIT is to make using Tower easier and less error 58 | prone. The current working idea that we have is to have a trait like: 59 | 60 | ```rust 61 | trait Service { 62 | type Response; 63 | 64 | async fn call(&self, request: Request) -> Self::Response; 65 | } 66 | ``` 67 | 68 | So far the experience has been pretty good working with AFIT with only a few 69 | well known issues like dynamic dispatch and send bounds. In addition, we have 70 | not looked at using `async-trait` because it forces `Send` bounds on our core 71 | trait which is an anti-goal for Tower as we would like to support both `Send` 72 | and `!Send` services. 73 | 74 | One of the biggest wins we have experienced moving to AFIT has been how 75 | ergonomic borrowing is within traits. For example, we have updated our retry 76 | middleware to use AFIT. The implementation goes from two 100+ loc files to one 77 | 15 line implementation that is simpler to understand and verify. This large 78 | change is due to not having to hand roll futures (which could be an artifact of 79 | when tower was created) and the ability to immutably borrow the service via 80 | `&self` rather than having to `Clone` the struct items. This is enabled by the 81 | ergonomic borrows that AFIT provides. 82 | 83 | ### Dynamic dispatch and Send 84 | 85 | Since, Tower stacks can become massive types and become very complicated for new 86 | Rust users to work with we generally recommend boxing the Tower stack (a stack 87 | is a group of Tower `Service`'s) after it has been constructed. This allows, for 88 | example, clients to remove their need for generics and simplify their public api 89 | while still leveraging Tower's composability. 90 | 91 | An example client: 92 | 93 | ```rust 94 | pub struct Client { 95 | svc: BoxService, 96 | } 97 | ``` 98 | 99 | Since, currently AFIT does not support dynamic traits we had to use a similar 100 | work around to the Microsoft and Async Builder + Provider API case studies. This 101 | workaround uses a second `DynService` trait that is object safe. Using this trait 102 | we can then define a `BoxService<'a, Req, Res, Err>` Tower service that completely 103 | erases the service type. 104 | 105 | ```rust 106 | trait DynService { 107 | type Res; 108 | type Error; 109 | 110 | fn call<'a>(&'a self, req: Req) -> BoxFuture<'a, Result> 111 | where 112 | Req: 'a; 113 | } 114 | 115 | pub struct BoxService<'a, Req, Res, Err> { 116 | b: Box>, 117 | } 118 | ``` 119 | 120 | This workaround works pretty well but falls short on one piece. The trait object 121 | within `BoxService` is `!Send`. If we try to add `Send` bounds to the trait 122 | object we eventually end up with a compiler error that says something like 123 | `impl Future` doesn't implement `Send` and we end up at [rust-lang/rust#103854] 124 | 125 | [rust-lang/rust#103854]: https://github.com/rust-lang/rust/issues/103854 126 | 127 | #### RTN 128 | 129 | Our issue above is that we have no way currently in Rust to express that some 130 | async fn in a trait is `Send`. To achieve this, we tried out a [branch] from 131 | @compiler-errors fork of rust. This branch implemented the return notation 132 | syntax for expressing a bounds on impl trait based trait functions like async 133 | fn. Using the synatx we were able to express this requirement to allow the trait 134 | object to be sendable. 135 | 136 | This blanket implementation allows us to have any `impl Service + Send` 137 | automatically implement our dyn version of our `Service` trait. 138 | 139 | ```rust 140 | impl DynService for T 141 | where 142 | T: Service + Send, 143 | T::Res: Send, 144 | T::Error: Send, 145 | Req: Send, 146 | { 147 | type Res = >::Res; 148 | type Error = >::Error; 149 | 150 | fn call<'a>(&'a self, req: Req) -> BoxFuture<'a, Result> 151 | where 152 | Req: 'a, 153 | { 154 | Box::pin(self.call(req)) 155 | } 156 | } 157 | ``` 158 | 159 | In addition, one of the core requirements of tower is to be flexible and we 160 | would like the core trait to be used in `Send` and `!Send` contexts. This means 161 | we require that the way we express sendness is bounded at the users call site 162 | rather than at the core trait level. 163 | 164 | Another note, because `async fn` tend to eagerly capture items this new approach 165 | requires that the `Requset` also be `Send`. This differs from how tower 166 | currently works because to bound a returned future by `Send` does not require 167 | that the input request type is `Send` as well since it does not lazily evaluate 168 | it. That said, in practice, if you end up using something like [`tower::buffer`] 169 | the `Request` type already needs to be `Send` to be able to send it over a 170 | channel. This is likely a non-issue anyways since current consumer code tends to 171 | use buffer to go from a `impl Service` to `impl Service + Clone`. 172 | 173 | [branch]: https://github.com/compiler-errors/rust/tree/rtn 174 | [`tower::buffer`]: https://docs.rs/tower/latest/tower/buffer/index.html 175 | 176 | ## Future areas of exploration 177 | 178 | - We have not yet implemented the full stacking implementation that we currently 179 | have with Tower. This means we have yet to potentailly run into any inference 180 | issues that could happen when using the `Layer` trait from Tower. 181 | 182 | ## References 183 | 184 | - https://github.com/LucioFranco/tower-playground/tree/lucio/sendable-box 185 | - https://github.com/tower-rs/tower 186 | -------------------------------------------------------------------------------- /evaluation/design/inline_async_fn.md: -------------------------------------------------------------------------------- 1 | # Inline async fn 2 | 3 | ## Status 4 | 5 | Seems unlikely to be adopted, but may be the seed of a better idea 6 | 7 | ## Status quo 8 | 9 | Until now, the only way to make an "async trait" be dyn-safe was to use a manual poll method. The [`AsyncRead`](https://docs.rs/futures/0.3.15/futures/io/trait.AsyncRead.html) trait in futures, for example, is as follows: 10 | 11 | ```rust 12 | pub trait AsyncRead { 13 | fn poll_read( 14 | self: Pin<&mut Self>, 15 | cx: &mut Context<'_>, 16 | buf: &mut [u8] 17 | ) -> Poll>; 18 | 19 | unsafe fn initializer(&self) -> Initializer { ... } 20 | 21 | fn poll_read_vectored( 22 | self: Pin<&mut Self>, 23 | cx: &mut Context<'_>, 24 | bufs: &mut [IoSliceMut<'_>] 25 | ) -> Poll> { ... } 26 | } 27 | ``` 28 | 29 | Implementing these traits is a significant hurdle, as it requires the use of `Pin`. It also means that people cannot leverage `.await` syntax or other niceties that they are accustomed to. (See [Alan hates writing a stream](https://rust-lang.github.io/wg-async-foundations/vision/status_quo/alan_hates_writing_a_stream.html) for a narrative description.) 30 | 31 | It would be nice if we could rework those traits to use `async fn`. Today that is only possible using the `async_trait` procedural macro: 32 | 33 | ```rust 34 | #[async_trait] 35 | pub trait AsyncRead { 36 | async fn read(&mut self, buf: &mut [u8]) -> Result; 37 | 38 | async fn poll_read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result; 39 | 40 | unsafe fn initializer(&self) -> Initializer { ... } 41 | 42 | } 43 | ``` 44 | 45 | Unfortunately, using async-trait has some downsides (see [Alan needs async in traits](https://rust-lang.github.io/wg-async-foundations/vision/status_quo/alan_needs_async_in_traits.html)). Most notably, the traits are rewritten to return a `Box`. For many purposes, this is fine, but in the case of foundational traits like `AsyncRead`, `AsyncDrop`, `AsyncWrite`, it is a significant hurdle: 46 | 47 | * These traits should be included in libcore and available to the no-std ecosystem, like `Read` and `Write`. 48 | * These traits are often on the performance "hot path" and forcing a memory allocation there could be significant for some applications. 49 | 50 | There are some other problems with the poll-based design. For example, the buffer supplied to `poll_read` can change in between invocations (and indeed existing adapters take advantage of this sometimes). This means that the traits cannot be used for [zero copy](https://github.com/rust-lang/wg-async-foundations/pull/207), although this is not the only hurdle. 51 | 52 | ### For drop especially, the state must be embedded within the self type 53 | 54 | If we want to have an async version of drop, it is really important that it does not return a separate future, but only makes use of state embedded within the type. This is because we might have a `Box` or some other type that implements `AsyncDrop`, but where we don't know the concrete type. We are going to want to be able to drop those, which implies that they will live on the stack, which implies that we have to know the contents of the resulting future to know if it is `Send`. 55 | 56 | ## Problem: returning a future 57 | 58 | The fundamental problem that makes `async fn` not dyn-safe (and the reason that allocation is required) is that every implementation of `AsyncRead` requires different amounts of state. The future that is returned is basically an enumeration with fields for each value that may be live across an `await` point, and naturally that will vary per implementation. This means that code which doesn't know the precise type that it is working with cannot predict how much space that type will require. One solution is certainly boxing, which sidesteps the problem by returning a pointer to memory in the heap. 59 | 60 | Using poll methods, as the existing traits do, sidesteps this in a different way: the poll methods basically require that any state that the `AsyncRead` impl requires across invocations of `poll` must be present within the `self` field itself. This is a perfectly valid solution for many applications, but figuring out that state and tracking it efficiently is tedious for users. 61 | 62 | ## Proposal: "inline" futures 63 | 64 | The idea is to allow users to opt-in to "inline futures". Users would write a `repr` attribute on traits that contain `async fn` methods (the attribute): 65 | 66 | ```rust 67 | #[repr(inline_async)] 68 | trait AsyncRead { 69 | async fn read(&mut self, buf: &mut [u8]) -> Result; 70 | 71 | ... 72 | } 73 | ``` 74 | 75 | The choice of `repr` is significant here: 76 | 77 | * Like repr on a struct, this is meant to be used for things that affect how the code is compiled and its efficiency, but which don't affect the "mental model" of how the trait works. 78 | * Like repr on a struct, using repr may imply some limitations on the things you can do with the trait in order to achieve those benefits. 79 | 80 | When a trait is as `repr(inline_async)`, the state for all of its async functions will be added into the type that implements the trait (this attribute could potentially also be used per method). This does imply some key limitations: 81 | 82 | * `repr(inline_async)` traits can only be implemented on structs or enums defined in the current crate. This allows the compiler to append those fields into the layout of that struct or enum. 83 | * `repr(inline_async)` traits can only contain `async fn` with `&mut self` methods. 84 | 85 | ## Desugaring 86 | 87 | The desugaring for an `inline_async` function is different. Rather than an `async fn` becoming a type that returns an `impl Future`, the `async fn` always returns a value of a fixed type. This is a kind of variant on [`Future::PollFn`], which will simply invoke the `poll_read` function each time it is called. What we want is *something* like this, although this doesn't quite work (and relies on unstable features Niko doesn't love): 88 | 89 | ```rust 90 | trait AsyncRead { 91 | // Standard async fn desugaring, with a twist: 92 | fn read(&mut self, buf: &mut [u8]) -> Future::PollFn< 93 | typeof(::poll_read) 94 | >; 95 | 96 | // 97 | fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; 98 | } 99 | ``` 100 | 101 | Basically the `read` method would 102 | 103 | * initialize the state of the future and then 104 | * construct a [`Future::PollFn`]-like struct that contains a pointer to the `poll_read` function. 105 | 106 | [`Future::PollFn`]: https://github.com/rust-lang/rust/issues/72302 107 | 108 | ## FAQ 109 | 110 | ### What's wrong with that desugaring? 111 | 112 | The desugaring is pretty close. It has the nice property that, when invoked with a known type, the `Future::PollFn` dispatches statically the poll function, so there is no dynamic dispatch or loss of efficiency. 113 | 114 | However, it also has a problem. The return type is still dependent on `Self`, so per our *existing* dyn Rules, that doesn't work. 115 | 116 | It should be possible to extend our dyn Rules, though. All that is needed is a bit of "adaptation glue" in the code that is included in the vtable so that it will convert from a `Future::PollFn` for some fixed `T` to one that uses a `fn` pointer. That seems eminently doable, but I'm not sure if it can be expressed in the language today. 117 | 118 | Pursuing this road might lead to a fundamental extension in dyn safety, which would be nice! 119 | 120 | ### What state is added precisely to the struct? 121 | 122 | * An integer recording the await point where the future is blocked 123 | * Fields for any data that outlives the await 124 | 125 | ### What if I don't want lots of state added to my struct? 126 | 127 | We could limit the use of variables live across an await. 128 | 129 | ### Could we extend this to other traits? 130 | 131 | e.g., simulacrum mentioned `-> impl Iterator` in a (dyn-safe) trait. Seems plausible. 132 | 133 | ### Why do you only permit `&mut self` methods? 134 | 135 | Since the state for the future is stored inline in the struct, we can only have one active future at a time. Using `&mut self` ensures that the poll function is only in use by one future at a time, since that future would be holding an `&mut` reference to the receiver. 136 | 137 | ### We would like to implement `AsyncRead` for all `&mut impl AsyncRead`, how can we enable that? 138 | 139 | I think this *should* be possible. The trick is that the poll function would just dispatch to another poll function. We might be able to support it by detecting the pattern of the `async fn` directly awaiting something reachable from self and supporting that for arbitrary types: 140 | 141 | ```rust 142 | impl AsyncRead for &mut T { 143 | async fn read(&mut self, buf: &mut [u8]) -> Result { 144 | T::read(self, buf).await 145 | } 146 | } 147 | ``` 148 | 149 | Basically this compiles to a `poll_read` that just tweaks dispatches to another `poll_read` with some derefs. 150 | 151 | ### Can you implement both AsyncRead and AsyncWrite for the same type with this technique? 152 | 153 | You can, but you can't simultaneously read and write from the same value. You would need a split-like API. 154 | -------------------------------------------------------------------------------- /explainer/async_fn_in_dyn_trait/generalizing_from_box_to_dynx.md: -------------------------------------------------------------------------------- 1 | # Generalizing from box with dynx structs 2 | 3 | ![planning rfc][] 4 | 5 | {{#include ../../badges.md}} 6 | 7 | So far our design has hardcoded the use of `Box` in the vtable. We can generalize this by introducing a new concept into the compiler; we currently call this a "dynx type"[^better-name]. Like closure types, dynx types are anonymous structs introduced by the compiler. Instead of returning a `Pin>`, the `next` function will return a `dynx Trait`, which represents "some kind of pointer to a `dyn Trait`". **Note that the `dynx Trait` types are anonymous and that the `dynx` syntax we are using here is for explanatory purposes only.** Users still work with pointer types like `&dyn Trait` , `&mut dyn Trait`, etc.[^user-facing] 8 | 9 | [^better-name]: Obviously the name "dynx type" is not great. We were considering "object type" (with e.g. `obj Trait` as the explanatory syntax) but we're not sure what to use here. 10 | [^user-facing]: It may make sense to use `dynx` as the basis for a user-facing feature at some point, but we are not proposing that here. 11 | 12 | At runtime, a `dynx Trait` struct has the same size as a `Box` (two machine words). If a `dynx Trait` were an ordinary struct, it might look like this: 13 | 14 | ```rust 15 | struct dynx Trait { 16 | data: *mut (), 17 | vtable: *mut (), 18 | } 19 | ``` 20 | 21 | Like a `Box`, it carries a vtable that lets us invoke the methods from `Trait` dynamically. Unlike a `Box`, it does not hardcode the memory allocation strategy. **Instead, a dynx vtable repurposes the "drop" function slot to mean "drop this pointer and its contents".** This allows `dynx Trait` types to be created from any kind of pointer, such as a `Box`, `&`, `&mut`, `Rc`, or `Arc`. When a `dynx Trait` struct is dropped, it invokes this drop function from its destructor: 22 | 23 | ```rust 24 | impl Drop for dynx Trait { 25 | fn drop(&mut self) { 26 | let drop_fn: fn(*mut ()) = self.vtable.drop_fn; 27 | drop_fn(self.data); 28 | } 29 | } 30 | ``` 31 | 32 | ## Using dynx in the vtable 33 | 34 | Now that we have `dynx`, we can define the vtable for an `AsyncIterator` almost exactly like we saw before, but using `dynx Future` instead of a pinned box: 35 | 36 | ```rust= 37 | struct AsyncIteratorVtable { 38 | ..., /* some stuff */ 39 | next: fn(&mut ()) -> dynx Future> 40 | // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 41 | // Look ma, no box! 42 | } 43 | ``` 44 | 45 | Calling `i.next()` for some `i: &mut dyn AsyncIterator` will now give back a `dynx` result: 46 | 47 | ```rust 48 | i.next().await 49 | 50 | // becomes 51 | 52 | let f: dynx Future> = i.next(); 53 | f.await 54 | ``` 55 | 56 | When the dynx `f` is dropped, its destructor will call the destructor from the vtable, freeing the backing memory in whatever way is appropriate for the kind of pointer that the `dynx` was constructed from. 57 | 58 | **What have we achieved?** By using `dynx` in the vtable, we have made it so that the caller doesn't know (or have to know) what memory management strategy is in use for the resulting trait object. It knows that it has an instance of some struct (`dynx Future`, specifically) that implements `Future`, is 2-words in size, and can be dropped in the usual way. 59 | 60 | ## The shim function builds the dynx 61 | 62 | In the previous section, we saw that using `dynx` in the vtable means that the caller no longer knows (or cares) what memory management strategy is in use. This section explains how the callee actually constructs the `dynx` that gets returned (and how impls can tweak that construction to choose the memory management strategy they want). 63 | 64 | Absent any other changes, the `impl` of `AsyncIter` contains an `async fn next()` that returns an `impl Future` which could have any size. Therefore, to construct a `dynx Future`, we are going to need an adaptive shim, just like we used in the `Pin` example. In fact, the default shim is almost exactly the same as we saw in that case. It allocates a pinned box to store the future and returns it. The main difference is that it converts this `Pin>` into a `dynx Future` before returning, rather than coercing to a `Pin>` return type (the next section will cover shim functions that don't allocate a box): 65 | 66 | ```rust 67 | // "pseudocode", you couldn't actually write this because there is no 68 | // user-facing syntax for the `dynx Future` type 69 | 70 | fn yielding_range_shim( 71 | this: &mut YieldingRangeIterator, 72 | ) -> dynx Future> { 73 | let boxed = Box::pin(::next(this)); 74 | 75 | // invoke the (inherent) `new` method that converts to a `dynx Future` 76 | ::new(boxed) 77 | } 78 | ``` 79 | 80 | The most interesting part of this function is the last line, which construct the `dynx Future` from its `new` function. Intuitively, the `new` function takes one argument, which must implement the trait `IntoRawPointer` (added by this design). The `IntoRawPointer` trait is implemented for smart pointer types int the standard library, like `Box`, `Pin`, and `Rc`, and represents "some kind of pointer" as well as "how to drop that pointer": 81 | 82 | ```rust 83 | // Not pseudocode, will be added to the stdlib and implemented 84 | // by various types, including `Box` and `Pin`. 85 | 86 | unsafe trait IntoRawPointer: Deref { 87 | /// Convert this pointer into a raw pointer to `Self::Target`. 88 | /// 89 | /// This raw pointer must be valid to dereference until `drop_raw` (below) is invoked; 90 | /// this trait is unsafe because the impl must ensure that to be true. 91 | fn into_raw(self) -> *mut Self::Target; 92 | 93 | /// Drops the smart pointer itself as well as the contents of the pointer. 94 | /// For example, when `Self = Box`, this will free the box as well as the 95 | /// `T` value. 96 | unsafe fn drop_raw(this: *mut Self::Target); 97 | } 98 | ``` 99 | 100 | The `::new` method just takes a parameter of type `impl IntoRawPointer` and invokes `into_raw` to convert it into a raw pointer. This raw pointer is then packaged up, together with a modified vtable for `Future`, into the `dynx` structure. The modified vtable is the same as a normal `Future` vtable, except that the "drop" slot is modified so that its drop function points to `IntoRawPointer::drop_raw`, which will be invoked on the data pointer when the `dynx` is dropped. In pseudocode, it looks like this: 101 | 102 | ```rust 103 | // "pseudocode", you couldn't actually write this because there is no 104 | // user-facing syntax for the `dynx Future` type; but conceptually this 105 | // inherent function exists (in the actual implementation, it may be inlined 106 | // by the compiler, since you could never name it 107 | 108 | struct dynx Future { 109 | data: *mut (), // underlying data pointer 110 | vtable: &'static FutureVtable, // "modified" vtable for `Future` for the underlying type 111 | } 112 | 113 | struct FutureVtable { 114 | /// Invokes `Future::poll` on the underlying data. 115 | /// 116 | /// Unsafe condition: Expects the output from `IntoRawPointer::into_raw` 117 | /// which must not have already been freed. 118 | poll_fn: unsafe fn(*mut (), cx: &mut Context<'_>) -> Ready<()>, 119 | 120 | /// Frees the memory for the pointer to future. 121 | /// 122 | /// Unsafe condition: Expects the output from `IntoRawPointer::into_raw` 123 | /// which must not have already been freed. 124 | drop_fn: unsafe fn(*mut ()), 125 | } 126 | 127 | impl dynx Future { 128 | fn new(from: RP) 129 | where 130 | RP: IntoRawPointer, 131 | RP::Target: Sized, // This must be sized so that we know we have a thin pointer. 132 | RP::Target: Future, // The target must implement the future trait. 133 | RP: Unpin, // Required because `Future` has a `Pin<&mut Self>` method, see discussion later. 134 | { 135 | let data = IntoRawPointer::into_raw(from); 136 | let vtable = FutureVtable { 137 | poll_fn: ::poll, 138 | drop_fn: |ptr| RP::drop_raw(ptr), 139 | }; // construct vtable 140 | dynx Future { 141 | data, vtable 142 | } 143 | } 144 | } 145 | 146 | impl Future for dynx Future { 147 | type Output = O; 148 | 149 | fn poll( 150 | self: Pin<&mut Self>, 151 | cx: &mut Context<'_>, 152 | ) -> Ready<()> { 153 | // Unsafety condition is met since... 154 | // 1. self.data was initialized in new and is otherwise never changed. 155 | // 2. drop must not yet have run or else self would not exist. 156 | unsafe { 157 | // Conceptually... 158 | let pin: Pin = Pin::new(self); // requires `RP: Unpin`. 159 | let pin_mut_self: Pin<&mut RP::Target> = pin.as_mut(); 160 | self.vtable.poll_fn(pin_mut_self, cx); 161 | self = pin.into_inner(); // XXX is this quite right? 162 | 163 | self.vtable.poll_fn(self.data, cx) 164 | } 165 | } 166 | } 167 | 168 | 169 | impl Drop for dynx Future { 170 | fn drop(&mut self) { 171 | // Unsafety condition is met since... 172 | // 1. self.data was initialized in new and is otherwise never changed. 173 | // 2. drop must not yet have run or else self would not exist. 174 | unsafe { 175 | self.vtable.drop_fn(self.data); 176 | } 177 | } 178 | } 179 | ``` -------------------------------------------------------------------------------- /archive/2021-phase_1_narrative.md: -------------------------------------------------------------------------------- 1 | # Async Fundamentals Mini Vision Doc 2 | 3 | > **WARNING:** This is an **archived** document, preserved for historical purposes. Go to the [explainer](../explainer.md) to see the most up-to-date material. 4 | 5 | Grace and Alan are working at BoggleyCorp, developing a network service using async I/O in Rust. Internally, they use a trait to manage http requests, which allows them to easily change to different sorts of providers: 6 | 7 | ```rust 8 | trait HttpRequestProvider { 9 | async fn request(&mut self, request: Request) -> anyhow::Result; 10 | } 11 | ``` 12 | 13 | They start out using `impl HttpRequest` and things seem to be working very well: 14 | 15 | ```rust 16 | async fn process_request( 17 | mut provider: impl HttpRequestProvider, 18 | request: Request, 19 | ) -> anyhow::Result<()> { 20 | let response = provider.request(request).await?; 21 | process_response(response).await; 22 | } 23 | ``` 24 | 25 | As they refactor their system, though, they decide they would like to store the provider in a `Context` type. When they create the struct, though, they realize that `impl Trait` syntax doesn't work in that context: 26 | 27 | ```rust 28 | struct Context { 29 | provider: impl HttpRequestProvider, 30 | } 31 | 32 | async fn process_request( 33 | mut provider: impl HttpRequestProvider, 34 | request: Request, 35 | ) -> anyhow::Result<()> { 36 | let cx = Context { 37 | provider 38 | }; 39 | // ... 40 | } 41 | ``` 42 | 43 | Alan looks to Grace, "What do we do now?" Grace says, "Well, we could make an explicit type parameter, but I think a `dyn` might be easier for us here." Alan says, "Oh, ok, that makes sense." He alters the struct to use a `Box`: 44 | 45 | ```rust 46 | struct Context { 47 | provider: Box, 48 | } 49 | ``` 50 | 51 | However, this gets a compilation error: "traits that contain async fn are not dyn safe". The compiler does, however, suggest that they check out the experimental [`dyner` crate](https://github.com/nikomatsakis/dyner). The README for the crate advises them to decorate their trait with `dyner::make_dyn`, and to give a name to use for the "dynamic dispatch" type: 52 | 53 | ```rust 54 | #[dyner::make_dyn(DynHttpRequestProvider)] 55 | trait HttpRequestProvider { 56 | async fn request(&mut self, request: Request) -> anyhow::Result; 57 | } 58 | ``` 59 | 60 | Following the readme, they also modify their `Context` struct like so: 61 | 62 | ```rust 63 | struct Context { 64 | provider: DynHttpRequestProvider<'static>, 65 | } 66 | 67 | async fn process_request( 68 | provider: impl HttpRequestProvider, 69 | request: Request, 70 | ) -> anyhow::Result<()> { 71 | let cx = Context { 72 | provider: DynHttpRequestProvider::from(provider), 73 | }; 74 | // ... 75 | } 76 | ``` 77 | 78 | However, the code doesn't compile just as is. They realize that the `Context` is only being used inside `process_request` and so they follow the "time-limited" pattern of adding a lifetime parameter `'me` to the context. This represents the period of time in which the context is in use: 79 | 80 | ```rust 81 | struct Context<'me> { 82 | provider: DynHttpRequestProvider<'me>, 83 | } 84 | 85 | async fn process_request( 86 | provider: impl HttpRequestProvider, 87 | request: Request, 88 | ) -> anyhow::Result<()> { 89 | let cx = Context { 90 | provider: DynHttpRequestProvider::from(provider), 91 | }; 92 | // ... 93 | } 94 | ``` 95 | 96 | At this point, they are able to invoke `provider.request()` as normal. 97 | 98 | ### Spawning tasks 99 | 100 | As their project expands, Alan and Grace realize that they are going to have process a number of requests in parallel. They insert some code to spawn off tasks: 101 | 102 | ```rust 103 | fn process_all_requests( 104 | provider: impl HttpRequestProvider + Clone, 105 | requests: Vec, 106 | ) -> anyhow::Result<()> { 107 | let handles = 108 | requests 109 | .into_iter() 110 | .map(|r| { 111 | let provider = provider.clone(); 112 | tokio::spawn(async move { 113 | process_request(provider, r).await; 114 | }) 115 | }) 116 | .collect(); 117 | tokio::join_all(handles).await 118 | } 119 | ``` 120 | 121 | However, when they write this, they get an error: 122 | 123 | ``` 124 | error: future cannot be sent between threads safely 125 | --> src/lib.rs:21:17 126 | | 127 | 21 | tokio::spawn(async move { 128 | | ^^^^^^^^^^^^ future created by async block is not `Send` 129 | | 130 | note: captured value is not `Send` 131 | --> src/lib.rs:22:37 132 | | 133 | 22 | process_request(provider, r).await; 134 | | ^^^^^^^^ has type `impl HttpRequestProvider + Clone` which is not `Send` 135 | note: required by a bound in `tokio::spawn` 136 | --> /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.13.0/src/task/spawn.rs:127:21 137 | | 138 | 127 | T: Future + Send + 'static, 139 | | ^^^^ required by this bound in `tokio::spawn` 140 | help: consider further restricting this bound 141 | | 142 | 13 | provider: impl HttpRequestProvider + Clone + std::marker::Send, 143 | | +++++++++++++++++++ 144 | ``` 145 | 146 | "Ah, right," thinks Alan. "I need to add `Send` to show that the provider is something we can send to another thread." 147 | 148 | ```rust 149 | async fn process_all_requests( 150 | provider: impl HttpRequestProvider + Clone + Send, 151 | // ^^^^ added this 152 | requests: Vec, 153 | ) { 154 | ... 155 | } 156 | ``` 157 | 158 | But when he adds that, he gets *another* error. This one is a bit more complex: 159 | 160 | ``` 161 | error[E0277]: the future returned by `HttpRequestProvider::request` (invoked on `impl HttpRequestProvider + Clone + Send`) cannot be sent between threads safely 162 | --> src/lib.rs:21:17 163 | | 164 | 21 | tokio::spawn(async move { 165 | | ^^^^^^^^^^^^ `::Request` cannot be sent between threads safely 166 | | 167 | note: future is not `Send` as it awaits another future which is not `Send` 168 | --> src/lib.rs:35:5 169 | | 170 | 35 | let response = provider.request(request).await?; 171 | | ^^^^^^^^^^^^^^^^^^^^^^^^^ this future is not `Send` 172 | note: required by a bound in `tokio::spawn` 173 | --> /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.13.0/src/task/spawn.rs:127:21 174 | | 175 | 127 | T: Future + Send + 'static, 176 | | ^^^^ required by this bound in `tokio::spawn` 177 | help: introduce a `provider: Send` bound to require this future to be send: 178 | | 179 | 13 ~ provider: impl HttpRequestProvider + Clone + Send,, 180 | | 181 | ``` 182 | 183 | Alan and Grace are a bit puzzled. They decide to to their friend Barbara, who knows Rust pretty well. 184 | 185 | Barbara looks over the error. She explains to them that when they call an async function -- even in a trait -- that results in a future. "Yeah, yeah", they say, "we know that." Barbara explains that this means that saying that `provider` is `Send` does not necessarily imply that the future resulting from `provider.request()` is `Send`. "Ah, that makes sense," says Alan. "But what is this suggestion at the bottom?" 186 | 187 | "Ah," says Barbara, "the notation `T: Foo` generally means the same as `T: Foo, T::Bar: Send`. In other words, it says that `T` implements `Foo` and that the associated type `Bar` is `Send`." 188 | 189 | "Oh, I see," says Alan, "I remember reading now that when we use an `async fn`, the compiler introduces an associated type with the same name as the function that represents the future that gets returned." 190 | 191 | "Right", answers Barbara. "So when you say `impl HttpRequestProvider` that means that the `request` method returns a `Send` value (a future, in this case)." 192 | 193 | Alan and Grace change their project as the compiler suggested, and things start to work: 194 | 195 | ```rust 196 | fn process_all_requests( 197 | provider: impl HttpRequestProvider + Clone, 198 | requests: Vec, 199 | ) -> anyhow::Result<()> { 200 | ... 201 | } 202 | ``` 203 | 204 | ### Dyner all the things (even non-async things) 205 | 206 | As Alan and Grace get used to dyner, they start using it for all kinds of dynamic dispatch, including some code which is not async. It takes getting used to, but dyner has some definite advantages over the builtin Rust functionality, particularly if you use it everywhere. For example, you can use `dyner` with traits that have `self` methods and even methods which take and return `impl Trait` values (so long as those traits also use dyner, and they use the standard naming convention of prefixing the name of the trait with `Dyn`): 207 | 208 | ```rust 209 | // The dyner crate re-exports standard library traits, along 210 | // with Dyn versions of them (e.g., DynIterator). You do have 211 | // to ensure that the `Iterator` and `DynIterator` are reachable 212 | // from the same path though: 213 | use dyner::iter::{Iterator, DynIterator}; 214 | 215 | #[dyner::make_dyn(DynWidgetStream)] 216 | trait WidgetTransform { 217 | fn transform( 218 | mut self, 219 | // ^^^^ not otherwise dyn safe 220 | w: impl Iterator, 221 | // ^^^^^^^^^^^^^ this would not ordinarily be dyn-safe 222 | ) -> impl Iterator; 223 | // ^^^^^^^^^^^^^ this would not ordinarily be dyn-safe 224 | } 225 | ``` 226 | 227 | ### Dyn trait objects for third-party traits 228 | 229 | Using `dyner`, Alan and Barbara are basically unblocked. After a while, though, they hit a problem. One of their dependencies defines a trait that has no `dyn` equivalent (XXX realistic example?): 230 | 231 | ```rust 232 | // In crate parser 233 | trait Parser { 234 | fn parse(&mut self, input: &str); 235 | } 236 | ``` 237 | 238 | They are able to manage this by declaring the "dyn" type themselves. To do so, however, they have to copy and paste the trait definition, which is kind of annoying: 239 | 240 | ```rust 241 | mod parser { 242 | dyner::make_dyn_extern { 243 | trait parser::Parser { 244 | fn parse(&mut self, input: &str); 245 | } 246 | } 247 | } 248 | ``` 249 | 250 | They can now use `crate::parser::{Parser, DynParser}` to get the trait and its Dyn equivalent; the `crate::parser::Parser` is a re-export. "Ah, so that's how dyner re-exports the traits from libstd", Grace realizes. 251 | 252 | ### What's missing? 253 | 254 | * Spawning and needing `Send` bounds 255 | * Appendix: how does dyner work? 256 | 257 | 258 | ### XXX 259 | 260 | * replacement for `where Self: Sized`? 261 | * a lot of those problems go away *but* 262 | * we can also offer an explicit `#[optimization]` annotation: 263 | * causes the default version to be reproduced for dyn types and exempts the function from all limitations 264 | * inherent async fn 265 | * `impl A + B` 266 | * `DynerAB`-- we could even do that, right? 267 | * 268 | * https://docs.rs/hyper/0.14.14/hyper/body/trait.HttpBody.html -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------