Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update documentation to discuss object safety in Rust 1.75+ #254

Merged
merged 1 commit into from
Dec 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 30 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,43 @@ Async trait methods
[<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:
The stabilization of async functions in traits in Rust 1.75 did not include
support for using traits containing async functions as `dyn Trait`. Trying to
use dyn with an async trait produces the following error:

```rust
trait MyTrait {
async fn f() {}
pub trait Trait {
async fn f(&self);
}

pub fn make() -> Box<dyn Trait> {
unimplemented!()
}
```

```console
error[E0706]: trait fns cannot be declared `async`
--> src/main.rs:4:5
error[E0038]: the trait `Trait` cannot be made into an object
--> src/main.rs:5:22
|
5 | pub fn make() -> Box<dyn Trait> {
| ^^^^^^^^^ `Trait` cannot be made into an object
|
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
--> src/main.rs:2:14
|
4 | async fn f() {}
| ^^^^^^^^^^^^^^^
1 | pub trait Trait {
| ----- this trait cannot be made into an object...
2 | async fn f(&self);
| ^ ...because method `f` is `async`
= help: consider moving `f` to another trait
```

This crate provides an attribute macro to make async fn in traits work.
This crate provides an attribute macro to make async fn in traits work with dyn
traits.

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.
of how this implementation differs from what the compiler and language deliver
natively.

[hard]: https://smallcultfollowing.com/babysteps/blog/2019/10/26/async-fn-in-traits-are-hard/

Expand All @@ -40,7 +54,9 @@ 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.
of traits and trait impls that contain async fn, and then they work. We get to
have `Vec<Box<dyn Advertisement + Sync>>` or `&[&dyn Advertisement]`, for
example.

```rust
use async_trait::async_trait;
Expand Down Expand Up @@ -95,8 +111,7 @@ can't be that badly broken.
- &#128077;&ensp;Associated types;
- &#128077;&ensp;Having async and non-async functions in the same trait;
- &#128077;&ensp;Default implementations provided by the trait;
- &#128077;&ensp;Elided lifetimes;
- &#128077;&ensp;Dyn-capable traits.
- &#128077;&ensp;Elided lifetimes.

<br>

Expand Down
4 changes: 0 additions & 4 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ fn main() {
if compiler < 47 {
println!("cargo:rustc-cfg=self_span_hack");
}

if compiler >= 75 && env::var_os("DOCS_RS").is_none() {
println!("cargo:rustc-cfg=native_async_fn_in_trait");
}
}

fn rustc_minor_version() -> Option<u32> {
Expand Down
50 changes: 32 additions & 18 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,45 @@
//!
//! <br>
//!
//! <h5>Type erasure for async trait methods</h5>
//! <h4>Type erasure for async trait methods</h4>
//!
//! 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:
//! The stabilization of async functions in traits in Rust 1.75 did not include
//! support for using traits containing async functions as `dyn Trait`. Trying
//! to use dyn with an async trait produces the following error:
//!
#![cfg_attr(not(native_async_fn_in_trait), doc = "```compile_fail")]
#![cfg_attr(native_async_fn_in_trait, doc = "```")]
//! trait MyTrait {
//! async fn f() {}
//! ```compile_fail
//! pub trait Trait {
//! async fn f(&self);
//! }
#![doc = "```"]
//!
//! pub fn make() -> Box<dyn Trait> {
//! unimplemented!()
//! }
//! ```
//!
//! ```text
//! error[E0706]: trait fns cannot be declared `async`
//! --> src/main.rs:4:5
//! error[E0038]: the trait `Trait` cannot be made into an object
//! --> src/main.rs:5:22
//! |
//! 5 | pub fn make() -> Box<dyn Trait> {
//! | ^^^^^^^^^ `Trait` cannot be made into an object
//! |
//! note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
//! --> src/main.rs:2:14
//! |
//! 4 | async fn f() {}
//! | ^^^^^^^^^^^^^^^
//! 1 | pub trait Trait {
//! | ----- this trait cannot be made into an object...
//! 2 | async fn f(&self);
//! | ^ ...because method `f` is `async`
//! = help: consider moving `f` to another trait
//! ```
//!
//! This crate provides an attribute macro to make async fn in traits work.
//! This crate provides an attribute macro to make async fn in traits work with
//! dyn traits.
//!
//! 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.
//! language deliver natively.
//!
//! [hard]: https://smallcultfollowing.com/babysteps/blog/2019/10/26/async-fn-in-traits-are-hard/
//!
Expand All @@ -43,7 +56,9 @@
//! 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.
//! top of traits and trait impls that contain async fn, and then they work. We
//! get to have `Vec<Box<dyn Advertisement + Sync>>` or `&[&dyn Advertisement]`,
//! for example.
//!
//! ```
//! use async_trait::async_trait;
Expand Down Expand Up @@ -111,8 +126,7 @@
//! > &#9745;&emsp;Associated types;<br>
//! > &#9745;&emsp;Having async and non-async functions in the same trait;<br>
//! > &#9745;&emsp;Default implementations provided by the trait;<br>
//! > &#9745;&emsp;Elided lifetimes;<br>
//! > &#9745;&emsp;Dyn-capable traits.<br>
//! > &#9745;&emsp;Elided lifetimes.<br>
//!
//! <br>
//!
Expand Down