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