Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Type erasure for async trait methods

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT
NotificationsYou must be signed in to change notification settings

workflow-rs/workflow-async-trait

 
 

Repository files navigation

githubcrates.iodocs.rsbuild status


This is a customized implementation of async trait that in addition toasync_trait exportsasync_trait_with_send andasync_trait_without_send attribute macros. This allows usingasync_trait(?Send) without the?Send qualifier, in-turn allowing use of#cfg[] conditional statements to alter betweenSend and?Send requirements based on features or platform architectures.


The initial round of stabilizations for the async/await language feature in Rust1.39 did not include support for async fn in traits. Trying to include an asyncfn in a trait produces the following error:

traitMyTrait{asyncfnf(){}}
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 towhy async fn in traits are hard for a deeper analysisof how this implementation differs from what the compiler and language hope todeliver in the future.


Example

This example implements the core of a highly effective advertising platformusing async fn in a trait.

The only thing to notice here is that we write an#[async_trait] macro on topof traits and trait impls that contain async fn, and then they work.

use async_trait::async_trait;#[async_trait]traitAdvertisement{asyncfnrun(&self);}structModal;#[async_trait]implAdvertisementforModal{asyncfnrun(&self){self.render_fullscreen().await;for _in0..4u16{remind_user_to_join_mailing_list().await;}self.hide_for_now().await;}}structAutoplayingVideo{media_url:String,}#[async_trait]implAdvertisementforAutoplayingVideo{asyncfnrun(&self){let stream =connect(&self.media_url).await;        stream.play().await;// Video probably persuaded user to join our mailing list!Modal.run().await;}}

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 yousee unexpected borrow checker errors, type errors, or warnings. There is no useofunsafe in the expanded code, so rest assured that if your code compiles itcan'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.

Explanation

Async fns get transformed into methods that returnPin<Box<dyn Future + Send + 'async_trait>> and delegate to a private async freestanding function.

For example theimpl Advertisement for AutoplayingVideo above would beexpanded as:

implAdvertisementforAutoplayingVideo{fnrun<'async_trait>(&'async_traitself,) ->Pin<Box<dyn std::future::Future<Output =()> +Send +'async_trait>>whereSelf:Sync +'async_trait,{asyncfnrun(_self:&AutoplayingVideo){/* the original method body */}Box::pin(run(self))}}

Non-threadsafe futures

Not all async traits need futures that aredyn Future + Send. To avoid havingSend and Sync bounds placed on the async trait methods, invoke the async traitmacro as#[async_trait(?Send)] on both the trait and the impl blocks.


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

typeElided<'a> =&'ausize;#[async_trait]traitTest{asyncfntest(not_okay:Elided,okay:&usize){}}
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'_.

#[async_trait]traitTest{// eitherasyncfntest<'e>(elided:Elided<'e>){}// orasyncfntest(elided:Elided<'_>){}}

Dyn traits

Traits with async methods can be used as trait objects as long as they meet theusual requirements for dyn -- no methods with type parameters, no self by value,no associated types, etc.

#[async_trait]pubtraitObjectSafe{asyncfnf(&self);asyncfng(&mutself);}implObjectSafeforMyType{...}let value:MyType = ...;let object =&valueas&dynObjectSafe;// make trait object

The one wrinkle is in traits that provide default implementations of asyncmethods. In order for the default implementation to produce a future that isSend, the async_trait macro must emit a bound ofSelf: Sync on trait methodsthat take&self and a boundSelf: Send on trait methods that take&mut self. An example of the former is visible in the expanded code in theexplanation 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 likethis:

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 implementationsfor some async methods, there are two resolutions. Either you can add Sendand/or Sync as supertraits (Send if there are&mut self methods with defaultimplementations, Sync if there are&self methods with default implementations)to constrain all implementors of the trait such that the default implementationsare applicable to them:

#[async_trait]pubtraitObjectSafe:Sync{// added supertraitasyncfncan_dyn(&self){}}let object =&valueas&dynObjectSafe;

or you can strike the problematic methods from your trait object by boundingthem withSelf: Sized:

#[async_trait]pubtraitObjectSafe{asyncfncannot_dyn(&self)whereSelf:Sized{}// presumably other methods}let object =&valueas&dynObjectSafe;

License

Licensed under either ofApache License, Version2.0 orMIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submittedfor inclusion in this crate by you, as defined in the Apache-2.0 license, shallbe dual licensed as above, without any additional terms or conditions.

About

Type erasure for async trait methods

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Rust100.0%

[8]ページ先頭

©2009-2025 Movatter.jp