Andrew Walbran | d1b91c7 | 2020-08-11 17:12:08 +0100 | [diff] [blame] | 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 |
Jeff Vander Stoep | 0caaeed | 2022-12-06 08:25:24 +0100 | [diff] [blame] | 5 | //! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs |
Andrew Walbran | d1b91c7 | 2020-08-11 17:12:08 +0100 | [diff] [blame] | 6 | //! |
| 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 | //! > ☑ Self by value, by reference, by mut reference, or no self;<br> |
| 108 | //! > ☑ Any number of arguments, any return value;<br> |
| 109 | //! > ☑ Generic type parameters and lifetime parameters;<br> |
| 110 | //! > ☑ Associated types;<br> |
| 111 | //! > ☑ Having async and non-async functions in the same trait;<br> |
| 112 | //! > ☑ Default implementations provided by the trait;<br> |
| 113 | //! > ☑ Elided lifetimes;<br> |
| 114 | //! > ☑ 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 Farrell | 75bf719 | 2023-08-16 19:13:20 +0000 | [diff] [blame^] | 121 | //! Send + 'async_trait>>` and delegate to a private async freestanding function. |
Andrew Walbran | d1b91c7 | 2020-08-11 17:12:08 +0100 | [diff] [blame] | 122 | //! |
| 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 Farrell | 75bf719 | 2023-08-16 19:13:20 +0000 | [diff] [blame^] | 129 | //! fn run<'async_trait>( |
| 130 | //! &'async_trait self, |
| 131 | //! ) -> Pin<Box<dyn core::future::Future<Output = ()> + Send + 'async_trait>> |
Andrew Walbran | d1b91c7 | 2020-08-11 17:12:08 +0100 | [diff] [blame] | 132 | //! where |
James Farrell | 75bf719 | 2023-08-16 19:13:20 +0000 | [diff] [blame^] | 133 | //! Self: Sync + 'async_trait, |
Andrew Walbran | d1b91c7 | 2020-08-11 17:12:08 +0100 | [diff] [blame] | 134 | //! { |
| 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 Galenson | aa9cbeb | 2021-08-09 10:24:16 -0700 | [diff] [blame] | 262 | //! default implementations) to constrain all implementors of the trait such that |
Andrew Walbran | d1b91c7 | 2020-08-11 17:12:08 +0100 | [diff] [blame] | 263 | //! 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 Farrell | 75bf719 | 2023-08-16 19:13:20 +0000 | [diff] [blame^] | 306 | #![doc(html_root_url = "https://docs.rs/async-trait/0.1.73")] |
Joel Galenson | 5448f37 | 2021-04-01 15:10:30 -0700 | [diff] [blame] | 307 | #![allow( |
| 308 | clippy::default_trait_access, |
| 309 | clippy::doc_markdown, |
Jeff Vander Stoep | 0caaeed | 2022-12-06 08:25:24 +0100 | [diff] [blame] | 310 | clippy::explicit_auto_deref, |
Joel Galenson | 5448f37 | 2021-04-01 15:10:30 -0700 | [diff] [blame] | 311 | clippy::if_not_else, |
| 312 | clippy::items_after_statements, |
Matthew Maurer | 58e0c00 | 2023-03-23 00:42:03 +0000 | [diff] [blame] | 313 | clippy::match_like_matches_macro, |
Joel Galenson | 5448f37 | 2021-04-01 15:10:30 -0700 | [diff] [blame] | 314 | clippy::module_name_repetitions, |
| 315 | clippy::shadow_unrelated, |
| 316 | clippy::similar_names, |
| 317 | clippy::too_many_lines |
| 318 | )] |
Haibo Huang | 7ff84c3 | 2020-11-24 20:53:12 -0800 | [diff] [blame] | 319 | |
Andrew Walbran | d1b91c7 | 2020-08-11 17:12:08 +0100 | [diff] [blame] | 320 | extern crate proc_macro; |
| 321 | |
| 322 | mod args; |
Jeff Vander Stoep | 0caaeed | 2022-12-06 08:25:24 +0100 | [diff] [blame] | 323 | mod bound; |
Andrew Walbran | d1b91c7 | 2020-08-11 17:12:08 +0100 | [diff] [blame] | 324 | mod expand; |
| 325 | mod lifetime; |
| 326 | mod parse; |
| 327 | mod receiver; |
James Farrell | 75bf719 | 2023-08-16 19:13:20 +0000 | [diff] [blame^] | 328 | mod verbatim; |
Andrew Walbran | d1b91c7 | 2020-08-11 17:12:08 +0100 | [diff] [blame] | 329 | |
| 330 | use crate::args::Args; |
| 331 | use crate::expand::expand; |
| 332 | use crate::parse::Item; |
| 333 | use proc_macro::TokenStream; |
| 334 | use quote::quote; |
| 335 | use syn::parse_macro_input; |
| 336 | |
| 337 | #[proc_macro_attribute] |
| 338 | pub 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 | } |