Skip to content

Commit

Permalink
Update documentation to discuss object safety in Rust 1.75+
Browse files Browse the repository at this point in the history
  • Loading branch information
dtolnay committed Dec 30, 2023
1 parent 034d8db commit c1576be
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 37 deletions.
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

0 comments on commit c1576be

Please sign in to comment.