| 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 | |