async_trait/
lib.rs

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//! > &#9745;&emsp;Self by value, by reference, by mut reference, or no self;<br>
125//! > &#9745;&emsp;Any number of arguments, any return value;<br>
126//! > &#9745;&emsp;Generic type parameters and lifetime parameters;<br>
127//! > &#9745;&emsp;Associated types;<br>
128//! > &#9745;&emsp;Having async and non-async functions in the same trait;<br>
129//! > &#9745;&emsp;Default implementations provided by the trait;<br>
130//! > &#9745;&emsp;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
234extern crate proc_macro;
235
236mod args;
237mod bound;
238mod expand;
239mod lifetime;
240mod parse;
241mod receiver;
242mod verbatim;
243
244use crate::args::Args;
245use crate::expand::expand;
246use crate::parse::Item;
247use proc_macro::TokenStream;
248use quote::quote;
249use syn::parse_macro_input;
250
251#[proc_macro_attribute]
252pub fn async_trait(args: TokenStream, input: TokenStream) -> TokenStream {
253    let args = parse_macro_input!(args as Args);
254    let mut item = parse_macro_input!(input as Item);
255    expand(&mut item, args.local);
256    TokenStream::from(quote!(#item))
257}