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 |
| 5 | //! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K |
| 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 + |
| 121 | //! Send + 'async>>` and delegate to a private async freestanding function. |
| 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 { |
| 129 | //! fn run<'async>( |
| 130 | //! &'async self, |
| 131 | //! ) -> Pin<Box<dyn core::future::Future<Output = ()> + Send + 'async>> |
| 132 | //! where |
| 133 | //! Self: Sync + 'async, |
| 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 | |
Joel Galenson | 5448f37 | 2021-04-01 15:10:30 -0700 | [diff] [blame] | 306 | #![allow( |
| 307 | clippy::default_trait_access, |
| 308 | clippy::doc_markdown, |
| 309 | clippy::if_not_else, |
| 310 | clippy::items_after_statements, |
| 311 | clippy::module_name_repetitions, |
| 312 | clippy::shadow_unrelated, |
| 313 | clippy::similar_names, |
| 314 | clippy::too_many_lines |
| 315 | )] |
Haibo Huang | 7ff84c3 | 2020-11-24 20:53:12 -0800 | [diff] [blame] | 316 | |
Andrew Walbran | d1b91c7 | 2020-08-11 17:12:08 +0100 | [diff] [blame] | 317 | extern crate proc_macro; |
| 318 | |
| 319 | mod args; |
| 320 | mod expand; |
| 321 | mod lifetime; |
| 322 | mod parse; |
| 323 | mod receiver; |
Andrew Walbran | d1b91c7 | 2020-08-11 17:12:08 +0100 | [diff] [blame] | 324 | |
| 325 | use crate::args::Args; |
| 326 | use crate::expand::expand; |
| 327 | use crate::parse::Item; |
| 328 | use proc_macro::TokenStream; |
| 329 | use quote::quote; |
| 330 | use syn::parse_macro_input; |
| 331 | |
| 332 | #[proc_macro_attribute] |
| 333 | pub fn async_trait(args: TokenStream, input: TokenStream) -> TokenStream { |
| 334 | let args = parse_macro_input!(args as Args); |
| 335 | let mut item = parse_macro_input!(input as Item); |
| 336 | expand(&mut item, args.local); |
| 337 | TokenStream::from(quote!(#item)) |
| 338 | } |