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
Copy file name to clipboardExpand all lines: src/vision/status_quo/alan_needs_async_in_traits.md
+17-15Lines changed: 17 additions & 15 deletions
Original file line number
Diff line number
Diff line change
@@ -10,15 +10,15 @@ If you would like to expand on this story, or adjust the answers to the FAQ, fee
10
10
11
11
Alan is working on a project with Barbara which has already gotten off to a [somewhat rocky start](./barbara_anguishes_over_http.md). He is working on abstracting away the HTTP implementation the library uses so that users can provide their own. He wants the user to implement an async trait called `HttpClient` which has one method `perform(request: Request) -> Response`. Alan tries to create the async trait:
12
12
13
-
```rust,ignore
13
+
```rust
14
14
traitHttpClient {
15
15
asyncfnperform(request:Request) ->Response;
16
16
}
17
17
```
18
18
19
19
When Alan tries to compile this, he gets an error:
@@ -31,27 +31,30 @@ When Alan tries to compile this, he gets an error:
31
31
```
32
32
33
33
Alan, who has been using Rust for a little while now, has learned to follow compiler error messages and adds `async-trait` to his `Cargo.toml`. Alan follows the README of `async-trait` and comes up with the following code:
34
-
'''rust
34
+
35
+
```rust
35
36
#[async_trait]
36
37
traitHttpClient {
37
38
asyncfnperform(request:Request) ->Response;
38
39
}
39
-
'''
40
+
```
41
+
40
42
Alan's code now compiles, but he also finds that his compile times have gone from under a second to around 6s, at least for a clean build.
41
43
42
-
**TODO::** Maybe add representive pre and post trait compile times here.
44
+
After Alan finishes adding the new trait, he shows his work off to Barbara and mentions he's happy with the work but is a little sad that compile times have worsened. Barbara, an experienced Rust developer, knows that using `async-trait` comes with some additional issues. In this particular case she is especially worried about tying their public API to a third-party dependency. Even though it is technically possible to implement traits annotated with `async_trait` without using `async_trait`, doing so in practice is very painful. For example `async_trait`:
45
+
46
+
* handles lifetimes for you if the returned future is tied to the lifetime of some inputs.
47
+
* boxes and pins the futures for you.
43
48
44
-
After Alan finishes adding the new trait, he shows his work off to Barbara and mentions he's happy with the work but is a little sad that compile times have worsened. Barbara, an experienced Rust developer, knows that using `async-trait` comes with some issues and in this case is especially worried about tying their public API to a third-party dependency. She decides to not worry Alan with this right now. Alan and Barbara are pretty happy with the results and go on to publish their crate which gets lots of users.
49
+
which implementer will have to manually handle if they don't use `async_trait`. She decides to not worry Alan with this right now. Alan and Barbara are pretty happy with the results and go on to publish their crate which gets lots of users.
45
50
46
51
Later on, a potential user of the library wants to use their library in a `no_std` context where they will be providing a custom HTTP stack. Alan and Barbara have done a pretty good job of limiting the use of standard library features and think it might be possible to support this use case. However, they quickly run into a show stopper: `async-trait` boxes all of the futures returned from a async trait function. They report this to Alan through an issue.
47
52
48
53
Alan, feeling (over-) confident in his Rust skills, decides to try to see if he can implement async traits without using `async-trait`.
49
54
50
55
```rust
51
-
structRequest(String);
52
-
53
56
traitHttpClient {
54
-
typeResponse:Future<Output=()>;
57
+
typeResponse:Future<Output=Response>;
55
58
56
59
fnperform(request:Request) ->Self::Response;
57
60
}
@@ -61,15 +64,15 @@ Alan seems to have something working, but when he goes to update the examples of
61
64
62
65
* use trait object:
63
66
64
-
```rust,ignore
67
+
```rust
65
68
structClientImpl;
66
69
67
70
implHttpClientforClientImpl {
68
-
type Response = Pin<Box<dyn Future<Output = ()>>>;
@@ -79,7 +82,7 @@ Alan seems to have something working, but when he goes to update the examples of
79
82
80
83
* implement `Future` trait manually, which isn't particulary easy/straight-forward for non-trivial cases, especially if it involves making other async calls (likely).
81
84
82
-
**TODO::** Would make sense for Alan to reach out to Barbara at this point and she should be able to give some good advice.
85
+
After a lot of thinking and discussion, Alan and Barbara accept that they won't be able to support `no_std` users of their library and add mention of this in crate documentation.
83
86
84
87
## 🤔 Frequently Asked Questions
85
88
@@ -88,7 +91,6 @@ Alan seems to have something working, but when he goes to update the examples of
88
91
*`async-trait` is awesome, but has some drawbacks
89
92
* compile time increases
90
93
* performance cost of boxing and dynamic dispatch
91
-
* lack of control over whether futures are `Send` or `Sync`
92
94
* not a standard solution so when this comes to language, it might break things
93
95
* Trying to have a more efficient implementation than `async-trait` is likely not possible.
94
96
@@ -97,7 +99,7 @@ Alan seems to have something working, but when he goes to update the examples of
97
99
*[Zeeshan](https://github.com/zeenix/) is looking for a way to implement async version of the [service-side zbus API](https://docs.rs/zbus/1.9.1/zbus/trait.Interface.html).
98
100
*[Ryan](https://github.com/rylev) had to use `async-trait` in an internal project.
99
101
100
-
## **Why did you choose Alan to tell this story?**
102
+
###**Why did you choose Alan to tell this story?**
101
103
102
104
We could have used Barbara here but she'd probably know some of the work-arounds (likely even the details on why they're needed) and wouldn't need help so it wouldn't make for a good story. Having said that, Barbara is involved in the story still so it's not a pure Alan story.
0 commit comments