diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
commit | 0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch) | |
tree | a31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /third_party/rust/async-trait/README.md | |
parent | Initial commit. (diff) | |
download | firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip |
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/async-trait/README.md')
-rw-r--r-- | third_party/rust/async-trait/README.md | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/third_party/rust/async-trait/README.md b/third_party/rust/async-trait/README.md new file mode 100644 index 0000000000..39e368bada --- /dev/null +++ b/third_party/rust/async-trait/README.md @@ -0,0 +1,262 @@ +Async trait methods +=================== + +[<img alt="github" src="https://img.shields.io/badge/github-dtolnay/async--trait-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/dtolnay/async-trait) +[<img alt="crates.io" src="https://img.shields.io/crates/v/async-trait.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/async-trait) +[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-async--trait-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/async-trait) +[<img alt="build status" src="https://img.shields.io/github/actions/workflow/status/dtolnay/async-trait/ci.yml?branch=master&style=for-the-badge" height="20">](https://github.com/dtolnay/async-trait/actions?query=branch%3Amaster) + +The initial round of stabilizations for the async/await language feature in Rust +1.39 did not include support for async fn in traits. Trying to include an async +fn in a trait produces the following error: + +```rust +trait MyTrait { + async fn f() {} +} +``` + +```console +error[E0706]: trait fns cannot be declared `async` + --> src/main.rs:4:5 + | +4 | async fn f() {} + | ^^^^^^^^^^^^^^^ +``` + +This crate provides an attribute macro to make async fn in traits work. + +Please refer to [*why async fn in traits are hard*][hard] for a deeper analysis +of how this implementation differs from what the compiler and language hope to +deliver in the future. + +[hard]: https://smallcultfollowing.com/babysteps/blog/2019/10/26/async-fn-in-traits-are-hard/ + +<br> + +## Example + +This example implements the core of a highly effective advertising platform +using async fn in a trait. + +The only thing to notice here is that we write an `#[async_trait]` macro on top +of traits and trait impls that contain async fn, and then they work. + +```rust +use async_trait::async_trait; + +#[async_trait] +trait Advertisement { + async fn run(&self); +} + +struct Modal; + +#[async_trait] +impl Advertisement for Modal { + async fn run(&self) { + self.render_fullscreen().await; + for _ in 0..4u16 { + remind_user_to_join_mailing_list().await; + } + self.hide_for_now().await; + } +} + +struct AutoplayingVideo { + media_url: String, +} + +#[async_trait] +impl Advertisement for AutoplayingVideo { + async fn run(&self) { + let stream = connect(&self.media_url).await; + stream.play().await; + + // Video probably persuaded user to join our mailing list! + Modal.run().await; + } +} +``` + +<br> + +## Supported features + +It is the intention that all features of Rust traits should work nicely with +\#\[async_trait\], but the edge cases are numerous. *Please file an issue if you +see unexpected borrow checker errors, type errors, or warnings.* There is no use +of `unsafe` in the expanded code, so rest assured that if your code compiles it +can't be that badly broken. + +- 👍 Self by value, by reference, by mut reference, or no self; +- 👍 Any number of arguments, any return value; +- 👍 Generic type parameters and lifetime parameters; +- 👍 Associated types; +- 👍 Having async and non-async functions in the same trait; +- 👍 Default implementations provided by the trait; +- 👍 Elided lifetimes; +- 👍 Dyn-capable traits. + +<br> + +## Explanation + +Async fns get transformed into methods that return `Pin<Box<dyn Future + Send + +'async_trait>>` and delegate to a private async freestanding function. + +For example the `impl Advertisement for AutoplayingVideo` above would be +expanded as: + +```rust +impl Advertisement for AutoplayingVideo { + fn run<'async_trait>( + &'async_trait self, + ) -> Pin<Box<dyn std::future::Future<Output = ()> + Send + 'async_trait>> + where + Self: Sync + 'async_trait, + { + async fn run(_self: &AutoplayingVideo) { + /* the original method body */ + } + + Box::pin(run(self)) + } +} +``` + +<br> + +## Non-threadsafe futures + +Not all async traits need futures that are `dyn Future + Send`. To avoid having +Send and Sync bounds placed on the async trait methods, invoke the async trait +macro as `#[async_trait(?Send)]` on both the trait and the impl blocks. + +<br> + +## Elided lifetimes + +Be aware that async fn syntax does not allow lifetime elision outside of `&` and +`&mut` references. (This is true even when not using #\[async_trait\].) +Lifetimes must be named or marked by the placeholder `'_`. + +Fortunately the compiler is able to diagnose missing lifetimes with a good error +message. + +```rust +type Elided<'a> = &'a usize; + +#[async_trait] +trait Test { + async fn test(not_okay: Elided, okay: &usize) {} +} +``` + +```console +error[E0726]: implicit elided lifetime not allowed here + --> src/main.rs:9:29 + | +9 | async fn test(not_okay: Elided, okay: &usize) {} + | ^^^^^^- help: indicate the anonymous lifetime: `<'_>` +``` + +The fix is to name the lifetime or use `'_`. + +```rust +#[async_trait] +trait Test { + // either + async fn test<'e>(elided: Elided<'e>) {} + // or + async fn test(elided: Elided<'_>) {} +} +``` + +<br> + +## Dyn traits + +Traits with async methods can be used as trait objects as long as they meet the +usual requirements for dyn -- no methods with type parameters, no self by value, +no associated types, etc. + +```rust +#[async_trait] +pub trait ObjectSafe { + async fn f(&self); + async fn g(&mut self); +} + +impl ObjectSafe for MyType {...} + +let value: MyType = ...; +let object = &value as &dyn ObjectSafe; // make trait object +``` + +The one wrinkle is in traits that provide default implementations of async +methods. In order for the default implementation to produce a future that is +Send, the async\_trait macro must emit a bound of `Self: Sync` on trait methods +that take `&self` and a bound `Self: Send` on trait methods that take `&mut +self`. An example of the former is visible in the expanded code in the +explanation section above. + +If you make a trait with async methods that have default implementations, +everything will work except that the trait cannot be used as a trait object. +Creating a value of type `&dyn Trait` will produce an error that looks like +this: + +```console +error: the trait `Test` cannot be made into an object + --> src/main.rs:8:5 + | +8 | async fn cannot_dyn(&self) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +``` + +For traits that need to be object safe and need to have default implementations +for some async methods, there are two resolutions. Either you can add Send +and/or Sync as supertraits (Send if there are `&mut self` methods with default +implementations, Sync if there are `&self` methods with default implementations) +to constrain all implementors of the trait such that the default implementations +are applicable to them: + +```rust +#[async_trait] +pub trait ObjectSafe: Sync { // added supertrait + async fn can_dyn(&self) {} +} + +let object = &value as &dyn ObjectSafe; +``` + +or you can strike the problematic methods from your trait object by bounding +them with `Self: Sized`: + +```rust +#[async_trait] +pub trait ObjectSafe { + async fn cannot_dyn(&self) where Self: Sized {} + + // presumably other methods +} + +let object = &value as &dyn ObjectSafe; +``` + +<br> + +#### License + +<sup> +Licensed under either of <a href="LICENSE-APACHE">Apache License, Version +2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option. +</sup> + +<br> + +<sub> +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this crate by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. +</sub> |