82 个版本
0.1.81 | 2024年7月7日 |
---|---|
0.1.79 | 2024年3月24日 |
0.1.76 | 2023年12月30日 |
0.1.74 | 2023年10月15日 |
0.1.6 | 2019年7月27日 |
#77 in Rust 模式
7,046,082 每月下载量
用于 16,807 个包 (6,264 个直接使用)
52KB
810 行
异步 trait 方法
Rust 1.75 中 traits 中异步函数的稳定化没有包括将包含异步函数的 traits 作为 dyn Trait
使用。尝试使用 dyn 与异步 trait 产生以下错误
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 <http://doc.rust-lang.net.cn/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
此包提供了一个属性宏,使 traits 中的异步 fn 可以与 dyn traits 一起工作。
请参阅 为什么 traits 中的异步 fn 很难 了解此实现与编译器和语言原生提供的内容有何不同。
示例
此示例通过在 traits 中使用异步 fn 实现了一个高度有效的广告平台的核心。
需要注意的是,我们只在 traits 和包含异步 fn 的 trait 实现上编写了 #[async_trait]
宏,然后它们就可以工作了。例如,我们可以得到 Vec<Box<dyn Advertisement + Sync>>
或 &[&dyn Advertisement]
。
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;
}
}
支持的功能
目标是让 Rust traits 的所有功能都很好地与 #[async_trait] 一起工作,但边缘情况众多。 如果您看到意外的借用检查器错误、类型错误或警告,请提交问题。 扩展代码中没有使用 unsafe
,所以请放心,如果您的代码可以编译,那么它不会太糟糕。
- 👍 按值、按引用、按可变引用或无 self
- 👍 任意数量的参数,任意返回值
- 👍 泛型类型参数和生命周期参数
- 👍 关联类型
- 👍 在同一个 trait 中有异步和非异步函数
- 👍 trait 提供的默认实现
- 👍 省略生命周期。
说明
异步函数被转换为返回类型为 Pin<Box<dyn Future + Send + 'async_trait>>
的方法,并将委托给异步块。
例如,上面的 impl Advertisement for AutoplayingVideo
将被展开为
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,
{
Box::pin(async move {
/* the original method body */
})
}
}
非线程安全的未来
并非所有的异步特质都需要 dyn Future + Send
类型的未来。为了避免在异步特质方法上放置 Send 和 Sync 的界限,将异步特质宏作为 #[async_trait(?Send)]
在特质的 impl 块上调用。
省略生命周期
请注意,异步函数语法不允许在 &
和 &mut
引用之外省略生命周期。(即使不使用 #[async_trait],这也适用。)生命周期必须被命名或用占位符 '_
标记。
幸运的是,编译器能够通过良好的错误信息诊断缺失的生命周期。
type Elided<'a> = &'a usize;
#[async_trait]
trait Test {
async fn test(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: `<'_>`
修复方法是命名生命周期或使用 '_
。
#[async_trait]
trait Test {
// either
async fn test<'e>(elided: Elided<'e>) {}
// or
async fn test(elided: Elided<'_>) {}
}
动态特质
具有异步方法特质可以用作特质对象,只要它们满足动态的常规要求——没有类型参数的方法,没有值 self,没有关联类型等。
#[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
一个难题在于提供异步方法默认实现的特质。为了使默认实现产生一个 Send 的未来,异步特质宏必须在取 &self
的特质方法上发出 Self: Sync
的界限,并在取 &mut self
的特质方法上发出 Self: Send
的界限。上述解释部分展开代码中的一个例子是前者。
如果你创建一个具有默认实现的异步方法的特质,除了特质不能用作特质对象外,一切都会正常工作。创建类型为 &dyn Trait
的值将产生一个类似这样的错误
error: the trait `Test` cannot be made into an object
--> src/main.rs:8:5
|
8 | async fn cannot_dyn(&self) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
对于需要是对象安全和需要为一些异步方法提供默认实现的特质,有两种解决方案。你可以添加 Send 和/或 Sync 作为超特质(如果存在具有默认实现的 &mut self
方法,Send 如果存在具有默认实现的 &self
方法)来约束特质的所有实现者,以便默认实现适用于它们
#[async_trait]
pub trait ObjectSafe: Sync { // added supertrait
async fn can_dyn(&self) {}
}
let object = &value as &dyn ObjectSafe;
或者你可以通过将它们与 Self: Sized
界限来从你的特质对象中删除有问题的方法
#[async_trait]
pub trait ObjectSafe {
async fn cannot_dyn(&self) where Self: Sized {}
// presumably other methods
}
let object = &value as &dyn ObjectSafe;
许可证
根据您的选择,本软件许可协议适用于Apache License, Version 2.0或MIT许可证。除非您明确声明,否则您根据Apache-2.0许可证定义的任何有意提交以包含在本软件包中的贡献,应按上述方式双许可,不得附加任何额外条款或条件。
依赖关系
约290–750KB
约18K SLoC