1 | //! [![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 |
5 | //! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs |
6 | //! |
7 | //! <br> |
8 | //! |
9 | //! <h4>Type erasure for async trait methods</h4> |
10 | //! |
11 | //! The stabilization of async functions in traits in Rust 1.75 did not include |
12 | //! support for using traits containing async functions as `dyn Trait`. Trying |
13 | //! to use dyn with an async trait produces the following error: |
14 | //! |
15 | //! ```compile_fail |
16 | //! pub trait Trait { |
17 | //! async fn f(&self); |
18 | //! } |
19 | //! |
20 | //! pub fn make() -> Box<dyn Trait> { |
21 | //! unimplemented!() |
22 | //! } |
23 | //! ``` |
24 | //! |
25 | //! ```text |
26 | //! error[E0038]: the trait `Trait` is not dyn compatible |
27 | //! --> src/main.rs:5:22 |
28 | //! | |
29 | //! 5 | pub fn make() -> Box<dyn Trait> { |
30 | //! | ^^^^^^^^^ `Trait` is not dyn compatible |
31 | //! | |
32 | //! note: for a trait to be dyn compatible it needs to allow building a vtable |
33 | //! for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility> |
34 | //! --> src/main.rs:2:14 |
35 | //! | |
36 | //! 1 | pub trait Trait { |
37 | //! | ----- this trait is not dyn compatible... |
38 | //! 2 | async fn f(&self); |
39 | //! | ^ ...because method `f` is `async` |
40 | //! = help: consider moving `f` to another trait |
41 | //! ``` |
42 | //! |
43 | //! This crate provides an attribute macro to make async fn in traits work with |
44 | //! dyn traits. |
45 | //! |
46 | //! Please refer to [*why async fn in traits are hard*][hard] for a deeper |
47 | //! analysis of how this implementation differs from what the compiler and |
48 | //! language deliver natively. |
49 | //! |
50 | //! [hard]: https://smallcultfollowing.com/babysteps/blog/2019/10/26/async-fn-in-traits-are-hard/ |
51 | //! |
52 | //! <br> |
53 | //! |
54 | //! # Example |
55 | //! |
56 | //! This example implements the core of a highly effective advertising platform |
57 | //! using async fn in a trait. |
58 | //! |
59 | //! The only thing to notice here is that we write an `#[async_trait]` macro on |
60 | //! top of traits and trait impls that contain async fn, and then they work. We |
61 | //! get to have `Vec<Box<dyn Advertisement + Sync>>` or `&[&dyn Advertisement]`, |
62 | //! for example. |
63 | //! |
64 | //! ``` |
65 | //! use async_trait::async_trait; |
66 | //! |
67 | //! #[async_trait] |
68 | //! trait Advertisement { |
69 | //! async fn run(&self); |
70 | //! } |
71 | //! |
72 | //! struct Modal; |
73 | //! |
74 | //! #[async_trait] |
75 | //! impl Advertisement for Modal { |
76 | //! async fn run(&self) { |
77 | //! self.render_fullscreen().await; |
78 | //! for _ in 0..4u16 { |
79 | //! remind_user_to_join_mailing_list().await; |
80 | //! } |
81 | //! self.hide_for_now().await; |
82 | //! } |
83 | //! } |
84 | //! |
85 | //! struct AutoplayingVideo { |
86 | //! media_url: String, |
87 | //! } |
88 | //! |
89 | //! #[async_trait] |
90 | //! impl Advertisement for AutoplayingVideo { |
91 | //! async fn run(&self) { |
92 | //! let stream = connect(&self.media_url).await; |
93 | //! stream.play().await; |
94 | //! |
95 | //! // Video probably persuaded user to join our mailing list! |
96 | //! Modal.run().await; |
97 | //! } |
98 | //! } |
99 | //! # |
100 | //! # impl Modal { |
101 | //! # async fn render_fullscreen(&self) {} |
102 | //! # async fn hide_for_now(&self) {} |
103 | //! # } |
104 | //! # |
105 | //! # async fn remind_user_to_join_mailing_list() {} |
106 | //! # |
107 | //! # struct Stream; |
108 | //! # async fn connect(_media_url: &str) -> Stream { Stream } |
109 | //! # impl Stream { |
110 | //! # async fn play(&self) {} |
111 | //! # } |
112 | //! ``` |
113 | //! |
114 | //! <br><br> |
115 | //! |
116 | //! # Supported features |
117 | //! |
118 | //! It is the intention that all features of Rust traits should work nicely with |
119 | //! #\[async_trait\], but the edge cases are numerous. Please file an issue if |
120 | //! you see unexpected borrow checker errors, type errors, or warnings. There is |
121 | //! no use of `unsafe` in the expanded code, so rest assured that if your code |
122 | //! compiles it can't be that badly broken. |
123 | //! |
124 | //! > ☑ Self by value, by reference, by mut reference, or no self;<br> |
125 | //! > ☑ Any number of arguments, any return value;<br> |
126 | //! > ☑ Generic type parameters and lifetime parameters;<br> |
127 | //! > ☑ Associated types;<br> |
128 | //! > ☑ Having async and non-async functions in the same trait;<br> |
129 | //! > ☑ Default implementations provided by the trait;<br> |
130 | //! > ☑ Elided lifetimes.<br> |
131 | //! |
132 | //! <br> |
133 | //! |
134 | //! # Explanation |
135 | //! |
136 | //! Async fns get transformed into methods that return `Pin<Box<dyn Future + |
137 | //! Send + 'async_trait>>` and delegate to an async block. |
138 | //! |
139 | //! For example the `impl Advertisement for AutoplayingVideo` above would be |
140 | //! expanded as: |
141 | //! |
142 | //! ``` |
143 | //! # const IGNORE: &str = stringify! { |
144 | //! impl Advertisement for AutoplayingVideo { |
145 | //! fn run<'async_trait>( |
146 | //! &'async_trait self, |
147 | //! ) -> Pin<Box<dyn core::future::Future<Output = ()> + Send + 'async_trait>> |
148 | //! where |
149 | //! Self: Sync + 'async_trait, |
150 | //! { |
151 | //! Box::pin(async move { |
152 | //! /* the original method body */ |
153 | //! }) |
154 | //! } |
155 | //! } |
156 | //! # }; |
157 | //! ``` |
158 | //! |
159 | //! <br><br> |
160 | //! |
161 | //! # Non-threadsafe futures |
162 | //! |
163 | //! Not all async traits need futures that are `dyn Future + Send`. To avoid |
164 | //! having Send and Sync bounds placed on the async trait methods, invoke the |
165 | //! async trait macro as `#[async_trait(?Send)]` on both the trait and the impl |
166 | //! blocks. |
167 | //! |
168 | //! <br> |
169 | //! |
170 | //! # Elided lifetimes |
171 | //! |
172 | //! Be aware that async fn syntax does not allow lifetime elision outside of `&` |
173 | //! and `&mut` references. (This is true even when not using #\[async_trait\].) |
174 | //! Lifetimes must be named or marked by the placeholder `'_`. |
175 | //! |
176 | //! Fortunately the compiler is able to diagnose missing lifetimes with a good |
177 | //! error message. |
178 | //! |
179 | //! ```compile_fail |
180 | //! # use async_trait::async_trait; |
181 | //! # |
182 | //! type Elided<'a> = &'a usize; |
183 | //! |
184 | //! #[async_trait] |
185 | //! trait Test { |
186 | //! async fn test(not_okay: Elided, okay: &usize) {} |
187 | //! } |
188 | //! ``` |
189 | //! |
190 | //! ```text |
191 | //! error[E0726]: implicit elided lifetime not allowed here |
192 | //! --> src/main.rs:9:29 |
193 | //! | |
194 | //! 9 | async fn test(not_okay: Elided, okay: &usize) {} |
195 | //! | ^^^^^^- help: indicate the anonymous lifetime: `<'_>` |
196 | //! ``` |
197 | //! |
198 | //! The fix is to name the lifetime or use `'_`. |
199 | //! |
200 | //! ``` |
201 | //! # use async_trait::async_trait; |
202 | //! # |
203 | //! # type Elided<'a> = &'a usize; |
204 | //! # |
205 | //! #[async_trait] |
206 | //! trait Test { |
207 | //! // either |
208 | //! async fn test<'e>(elided: Elided<'e>) {} |
209 | //! # } |
210 | //! # #[async_trait] |
211 | //! # trait Test2 { |
212 | //! // or |
213 | //! async fn test(elided: Elided<'_>) {} |
214 | //! } |
215 | //! ``` |
216 | |
217 | #![doc (html_root_url = "https://docs.rs/async-trait/0.1.88" )] |
218 | #![allow ( |
219 | clippy::default_trait_access, |
220 | clippy::doc_markdown, |
221 | clippy::elidable_lifetime_names, |
222 | clippy::explicit_auto_deref, |
223 | clippy::if_not_else, |
224 | clippy::items_after_statements, |
225 | clippy::match_like_matches_macro, |
226 | clippy::module_name_repetitions, |
227 | clippy::needless_lifetimes, |
228 | clippy::shadow_unrelated, |
229 | clippy::similar_names, |
230 | clippy::too_many_lines, |
231 | clippy::trivially_copy_pass_by_ref |
232 | )] |
233 | |
234 | extern crate proc_macro; |
235 | |
236 | mod args; |
237 | mod bound; |
238 | mod expand; |
239 | mod lifetime; |
240 | mod parse; |
241 | mod receiver; |
242 | mod verbatim; |
243 | |
244 | use crate::args::Args; |
245 | use crate::expand::expand; |
246 | use crate::parse::Item; |
247 | use proc_macro::TokenStream; |
248 | use quote::quote; |
249 | use syn::parse_macro_input; |
250 | |
251 | #[proc_macro_attribute ] |
252 | pub fn async_trait (args: TokenStream, input: TokenStream) -> TokenStream { |
253 | let args: Args = parse_macro_input!(args as Args); |
254 | let mut item: Item = parse_macro_input!(input as Item); |
255 | expand(&mut item, is_local:args.local); |
256 | TokenStream::from(quote!(#item)) |
257 | } |
258 | |