async-trait #
Rust trait 中如果含有 async fn 函数,则是通过隐式创建一个关联类型来实现 async fn 返回的 impl Future 类型值的,这会带来几个问题:
- 该 trait 不在是
dyn-compatible/object-safe的,不能使用它定义trait object; async fn返回的impl Future类型对象没有实现各种 AutoTrait,如 Send/Sync/Unpin 等,不能直接用于异步上下文中进行 .await;
pub trait Trait {
async fn f(&self);
}
pub fn make() -> Box<dyn Trait> {
unimplemented!()
}
// 报错
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
|
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
async-trait 则提供了 attribute macro 的解决方案:它将 async fn 函数返回的值类型由 impl Future 转换为 Pin<Box<dyn Future + Send +'_>>,从而满足多线程异步场景。
使用方式:在定义和实现包含 async 函数或方法的 trait 时,添加 #[async_trait] 属性宏。
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;
}
}