You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
**Status:** This functionality was described in [RFC 3185], merged on Dec 7, 2021, and was covered in detail in our [previous blog post][pp].
46
+
**Status:** This functionality was described in [RFC 3185], merged on Dec 7, 2021, and is available in nightly. It was covered in detail in our [previous blog post][pp].
## MVP Part 2: Send bounds and associated return types
52
54
53
-
There is one complication that arises when using async functions in traits that doesn't arise with sync functions. Many async runtimes -- notably including the default configurations of [Tokio] and [async-std] -- use a workstealing thread scheduler. This means that futures may move between worker threads dynamically to achieve load balancing. As a result, the future must only capture `Send` data.
55
+
There is one complication that arises when using async functions in traits that doesn't arise with sync functions. Many async runtimes -- notably including the default configurations of [Tokio] and [async-std] -- use a work stealing thread scheduler. This means that futures may move between worker threads dynamically to achieve load balancing. As a result, the future must only capture `Send` data.
54
56
55
57
[Tokio]: https://tokio.rs/
56
58
57
59
[async-std]: https://async.rs/
58
60
59
-
If you author a generic async function that spawns tasks on one of those runtimes, however, you will start to get compilation errors ([playground](XXX)):
61
+
If you author a generic async function that spawns tasks on one of those runtimes, however, you will start to get compilation errors ([playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=c033cf6174ff7d44e5673ecd254e6e69)):
60
62
61
63
```rust
62
64
asyncfndo_health_check_par(hc:implHealthCheck) {
@@ -101,9 +103,11 @@ In our [previous post][pp], we [hypothesized](https://blog.rust-lang.org/inside-
101
103
102
104
**Status:** Associated return types have an experimental implementation and we are currently drafting an RFC. There are several open bugs that will need to be fixed.
## MVP Part 3: "impl trait in traits" (return position)
105
109
106
-
All across Rust, an async function is "syntactic sugar" for a function that returns an `impl Future` -- and async functions in traits are no exception. As part of the MVP, we plan to stabilize the use of `-> impl Trait` notation in traits.
110
+
In Rust an async function is "syntactic sugar" for a function that returns an `impl Future`, and async functions in traits are no exception. As part of the MVP, we plan to stabilize the use of `-> impl Trait` notation in traits and trait impls.
107
111
108
112
Impl trait in traits has all kinds of uses, but one common one for async programming is to avoid capturing all of the function arguments by doing some amount of sync work and then returning a future for the rest. For example, this `LaunchService` trait declares a `launch` function that does not capture `self` (similar to the existing Tower [`Service`] trait):
109
113
@@ -121,15 +125,29 @@ trait LaunchService {
121
125
}
122
126
```
123
127
128
+
Since `async fn` is sugar for a regular function returning `impl Future`, these two syntactic forms will work interchangeably.
Even though the need for "impl trait in traits" comes up a lot in async, they are a general feature that will be useful in many contexts having nothing to do with async (for example, returning iterators from trait methods).
125
141
126
142
**Status:** Return-position impl trait in traits have an experimental implementation and are described in the recently opened [RFC 3425].
To evaluate the utility of this MVP, the working group collected [five case studies][] covering the [builder-provider pattern used in the AWS SDK](https://rust-lang.github.io/async-fundamentals-initiative/evaluation/case-studies/builder-provider-api.html#dynamic-dispatch-behind-the-api); the potential use of async function in traits in [tower][cst] and the actual use in [embassy][cse], the [Fuchsia networking stack][] and [an internal Microsoft tool][]. These studies validated that the above functionality is sufficient to use async function in traits for all kinds of things, though some situations require workarounds (hence the "MVP" title).
150
+
To evaluate the utility of this MVP, the working group collected [five case studies] covering the [builder-provider pattern used in the AWS SDK](https://rust-lang.github.io/async-fundamentals-initiative/evaluation/case-studies/builder-provider-api.html#dynamic-dispatch-behind-the-api); the potential use of async function in traits in [tower][cst] and the actual use in [embassy][cse], the [Fuchsia networking stack] and [an internal Microsoft tool]. These studies validated that the above functionality is sufficient to use async function in traits for all kinds of things, though some situations require workarounds (hence the "MVP" title).
@@ -147,9 +165,11 @@ The case studies revealed two situations that the MVP doesn't support very well,
147
165
148
166
### Modeling dynamic dispatch
149
167
150
-
In the MVP, traits that use async functions are not "dyn safe", meaning that they don't support dynamic dispatch. So e.g. given the `HealthCheck` trait we saw earlier, one could not write `Box<dyn HealthCheck>`. At first, this seems like a crucial limitation, since many of the use cases require dynamic dispatch! But it turns out that there is a workaround. One can define an "erased" trait internally to your crate that enables dynamic dispatch. The process was pioneered by crates like [erased serde][] and is explained in detail in the [builder-provider case study][].
168
+
In the MVP, traits that use async functions are not "dyn safe", meaning that they don't support dynamic dispatch. So e.g. given the `HealthCheck` trait we saw earlier, one could not write `Box<dyn HealthCheck>`.
169
+
170
+
At first, this seems like a crucial limitation, since many of the use cases require dynamic dispatch! But it turns out that there is a workaround. One can define an "erased" trait internally to your crate that enables dynamic dispatch. The process was pioneered by crates like [erased serde] and is explained in detail in the [builder-provider case study].
151
171
152
-
In the future, async fn should work with `dyn Trait` directly.
172
+
To make this workaround easier in the near term, we hope to provide a proc macro to automate it. In the future, async fn should work with `dyn Trait` directly.
[builder-provider case study]: https://rust-lang.github.io/async-fundamentals-initiative/evaluation/case-studies/builder-provider-api.html#dynamic-dispatch-behind-the-api
@@ -172,7 +192,7 @@ where
172
192
{}
173
193
```
174
194
175
-
Using a pattern like this means you can write `T: SendHealthCheck`. In the future, something like [trait transformers][] may provide a more concise syntax.
195
+
Using a pattern like this means you can write `T: SendHealthCheck`. We hope to provide a proc macro to write these trait aliases for you. In the future, something like [trait transformers] may provide a more concise syntax.
0 commit comments