trait_async/
lib.rs

1//! <h5>Type erasure for async trait methods</h5>
2//!
3//! The async/await language feature is on track for an initial round of
4//! stabilizations in Rust 1.39 (tracking issue: [rust-lang/rust#62149]), but
5//! this does not include support for async fn in traits. Trying to include an
6//! async fn in a trait produces the following error:
7//!
8//! [rust-lang/rust#62149]: https://github.com/rust-lang/rust/issues/62149
9//!
10//! ```compile_fail
11//! trait MyTrait {
12//!     async fn f() {}
13//! }
14//! ```
15//!
16//! ```text
17//! error[E0706]: trait fns cannot be declared `async`
18//!  --> src/main.rs:4:5
19//!   |
20//! 4 |     async fn f() {}
21//!   |     ^^^^^^^^^^^^^^^
22//! ```
23//!
24//! This crate provides an attribute macro to make async fn in traits work.
25//!
26//! Please refer to [*why async fn in traits are hard*][hard] for a deeper
27//! analysis of how this implementation differs from what the compiler and
28//! language hope to deliver in the future.
29//!
30//! [hard]: https://smallcultfollowing.com/babysteps/blog/2019/10/26/async-fn-in-traits-are-hard/
31//!
32//! <br>
33//!
34//! # Example
35//!
36//! This example implements the core of a highly effective advertising platform
37//! using async fn in a trait.
38//!
39//! The only thing to notice here is that we write an `#[trait_async]` macro on
40//! top of traits and trait impls that contain async fn, and then they work.
41//!
42//! ```
43//! use trait_async::trait_async;
44//!
45//! #[trait_async]
46//! trait Advertisement {
47//!     async fn run(&self);
48//! }
49//!
50//! struct Modal;
51//!
52//! #[trait_async]
53//! impl Advertisement for Modal {
54//!     async fn run(&self) {
55//!         self.render_fullscreen().await;
56//!         for _ in 0..4u16 {
57//!             remind_user_to_join_mailing_list().await;
58//!         }
59//!         self.hide_for_now().await;
60//!     }
61//! }
62//!
63//! struct AutoplayingVideo {
64//!     media_url: String,
65//! }
66//!
67//! #[trait_async]
68//! impl Advertisement for AutoplayingVideo {
69//!     async fn run(&self) {
70//!         let stream = connect(&self.media_url).await;
71//!         stream.play().await;
72//!
73//!         // Video probably persuaded user to join our mailing list!
74//!         Modal.run().await;
75//!     }
76//! }
77//! #
78//! # impl Modal {
79//! #     async fn render_fullscreen(&self) {}
80//! #     async fn hide_for_now(&self) {}
81//! # }
82//! #
83//! # async fn remind_user_to_join_mailing_list() {}
84//! #
85//! # struct Stream;
86//! # async fn connect(_media_url: &str) -> Stream { Stream }
87//! # impl Stream {
88//! #     async fn play(&self) {}
89//! # }
90//! ```
91//!
92//! <br><br>
93//!
94//! # Supported features
95//!
96//! It is the intention that all features of Rust traits should work nicely with
97//! #\[trait_async\], but the edge cases are numerous. Please file an issue if
98//! you see unexpected borrow checker errors, type errors, or warnings. There is
99//! no use of `unsafe` in the expanded code, so rest assured that if your code
100//! compiles it can't be that badly broken.
101//!
102//! > &#9745;&emsp;Self by value, by reference, by mut reference, or no self;<br>
103//! > &#9745;&emsp;Any number of arguments, any return value;<br>
104//! > &#9745;&emsp;Generic type parameters and lifetime parameters;<br>
105//! > &#9745;&emsp;Associated types;<br>
106//! > &#9745;&emsp;Having async and non-async functions in the same trait;<br>
107//! > &#9745;&emsp;Default implementations provided by the trait;<br>
108//! > &#9745;&emsp;Elided lifetimes;<br>
109//! > &#9745;&emsp;Dyn-capable traits.<br>
110//!
111//! <br>
112//!
113//! # Explanation
114//!
115//! Async fns get transformed into methods that return `Pin<Box<dyn Future +
116//! Send + 'async>>` and delegate to a private async freestanding function.
117//!
118//! For example the `impl Advertisement for AutoplayingVideo` above would be
119//! expanded as:
120//!
121//! ```
122//! # const IGNORE: &str = stringify! {
123//! impl Advertisement for AutoplayingVideo {
124//!     fn run<'async>(
125//!         &'async self,
126//!     ) -> Pin<Box<dyn core::future::Future<Output = ()> + Send + 'async>>
127//!     where
128//!         Self: Sync + 'async,
129//!     {
130//!         async fn run(_self: &AutoplayingVideo) {
131//!             /* the original method body */
132//!         }
133//!
134//!         Box::pin(run(self))
135//!     }
136//! }
137//! # };
138//! ```
139//!
140//! <br><br>
141//!
142//! # Non-threadsafe futures
143//!
144//! Not all async traits need futures that are `dyn Future + Send`. To avoid
145//! having Send and Sync bounds placed on the async trait methods, invoke the
146//! async trait macro as `#[trait_async(?Send)]` on both the trait and the impl
147//! blocks.
148//!
149//! <br>
150//!
151//! # Elided lifetimes
152//!
153//! Be aware that async fn syntax does not allow lifetime elision outside of `&`
154//! and `&mut` references. (This is true even when not using #\[trait_async\].)
155//! Lifetimes must be named or marked by the placeholder `'_`.
156//!
157//! Fortunately the compiler is able to diagnose missing lifetimes with a good
158//! error message.
159//!
160//! ```compile_fail
161//! # use trait_async::trait_async;
162//! #
163//! type Elided<'a> = &'a usize;
164//!
165//! #[trait_async]
166//! trait Test {
167//!     async fn test(not_okay: Elided, okay: &usize) {}
168//! }
169//! ```
170//!
171//! ```text
172//! error[E0726]: implicit elided lifetime not allowed here
173//!  --> src/main.rs:9:29
174//!   |
175//! 9 |     async fn test(not_okay: Elided, okay: &usize) {}
176//!   |                             ^^^^^^- help: indicate the anonymous lifetime: `<'_>`
177//! ```
178//!
179//! The fix is to name the lifetime or use `'_`.
180//!
181//! ```
182//! # use trait_async::trait_async;
183//! #
184//! # type Elided<'a> = &'a usize;
185//! #
186//! #[trait_async]
187//! trait Test {
188//!     // either
189//!     async fn test<'e>(elided: Elided<'e>) {}
190//! # }
191//! # #[trait_async]
192//! # trait Test2 {
193//!     // or
194//!     async fn test(elided: Elided<'_>) {}
195//! }
196//! ```
197//!
198//! <br><br>
199//!
200//! # Dyn traits
201//!
202//! Traits with async methods can be used as trait objects as long as they meet
203//! the usual requirements for dyn -- no methods with type parameters, no self
204//! by value, no associated types, etc.
205//!
206//! ```
207//! # use trait_async::trait_async;
208//! #
209//! #[trait_async]
210//! pub trait ObjectSafe {
211//!     async fn f(&self);
212//!     async fn g(&mut self);
213//! }
214//!
215//! # const IGNORE: &str = stringify! {
216//! impl ObjectSafe for MyType {...}
217//!
218//! let value: MyType = ...;
219//! # };
220//! #
221//! # struct MyType;
222//! #
223//! # #[trait_async]
224//! # impl ObjectSafe for MyType {
225//! #     async fn f(&self) {}
226//! #     async fn g(&mut self) {}
227//! # }
228//! #
229//! # let value: MyType = MyType;
230//! let object = &value as &dyn ObjectSafe;  // make trait object
231//! ```
232//!
233//! The one wrinkle is in traits that provide default implementations of async
234//! methods. In order for the default implementation to produce a future that is
235//! Send, the trait_async macro must emit a bound of `Self: Sync` on trait
236//! methods that take `&self` and a bound `Self: Send` on trait methods that
237//! take `&mut self`. An example of the former is visible in the expanded code
238//! in the explanation section above.
239//!
240//! If you make a trait with async methods that have default implementations,
241//! everything will work except that the trait cannot be used as a trait object.
242//! Creating a value of type `&dyn Trait` will produce an error that looks like
243//! this:
244//!
245//! ```text
246//! error: the trait `Test` cannot be made into an object
247//!  --> src/main.rs:8:5
248//!   |
249//! 8 |     async fn cannot_dyn(&self) {}
250//!   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
251//! ```
252//!
253//! For traits that need to be object safe and need to have default
254//! implementations for some async methods, there are two resolutions. Either
255//! you can add Send and/or Sync as supertraits (Send if there are `&mut self`
256//! methods with default implementations, Sync if there are `&self` methods with
257//! default implementions) to constrain all implementors of the trait such that
258//! the default implementations are applicable to them:
259//!
260//! ```
261//! # use trait_async::trait_async;
262//! #
263//! #[trait_async]
264//! pub trait ObjectSafe: Sync {  // added supertrait
265//!     async fn can_dyn(&self) {}
266//! }
267//! #
268//! # struct MyType;
269//! #
270//! # #[trait_async]
271//! # impl ObjectSafe for MyType {}
272//! #
273//! # let value = MyType;
274//!
275//! let object = &value as &dyn ObjectSafe;
276//! ```
277//!
278//! or you can strike the problematic methods from your trait object by
279//! bounding them with `Self: Sized`:
280//!
281//! ```
282//! # use trait_async::trait_async;
283//! #
284//! #[trait_async]
285//! pub trait ObjectSafe {
286//!     async fn cannot_dyn(&self) where Self: Sized {}
287//!
288//!     // presumably other methods
289//! }
290//! #
291//! # struct MyType;
292//! #
293//! # #[trait_async]
294//! # impl ObjectSafe for MyType {}
295//! #
296//! # let value = MyType;
297//!
298//! let object = &value as &dyn ObjectSafe;
299//! ```
300
301extern crate proc_macro;
302
303mod args;
304mod expand;
305mod lifetime;
306mod parse;
307mod receiver;
308
309use crate::args::Args;
310use crate::expand::expand;
311use crate::parse::Item;
312use proc_macro::TokenStream;
313use quote::quote;
314use syn::parse_macro_input;
315
316#[proc_macro_attribute]
317pub fn trait_async(args: TokenStream, input: TokenStream) -> TokenStream {
318    let args = parse_macro_input!(args as Args);
319    let mut item = parse_macro_input!(input as Item);
320    expand(&mut item, args.local);
321    TokenStream::from(quote!(#item))
322}