2 个版本
0.1.36 | 2020年7月7日 |
---|---|
0.1.31 | 2020年7月7日 |
#638 在 过程宏
每月508 次下载
用于 11 个Crates(直接使用9个)
46KB
868 行
支持 Sync
的异步特性方法
该包完全基于 https://github.com/dtolnay/async-trait,除了增加一个小补丁,允许在这些future上派生 Sync
(参见 https://github.com/dtolnay/async-trait/pull/96),这在目前是必须的,直到在 https://github.com/dtolnay/async-trait/issues/77 中解决的更广泛生态问题得到解决。
lib.rs
:
异步特性方法的类型擦除
Rust 1.39中异步/await语言特性的初始稳定版本不包括对特性中异步fn的支持。尝试在特性中包含异步fn将产生以下错误
trait MyTrait {
async fn f() {}
}
error[E0706]: trait fns cannot be declared `async`
--> src/main.rs:4:5
|
4 | async fn f() {}
| ^^^^^^^^^^^^^^^
该包提供了一个属性宏,使特性中的异步fn能够正常工作。
请参阅为什么 trait 中的异步 fn 很难,以深入了解这种实现方式与编译器和语言在未来的预期有何不同。
示例
此示例使用 trait 中的异步 fn 实现了一个高度有效的广告平台的核心。
需要注意的是,我们在包含异步 fn 的 trait 和 trait impl 上编写了一个 #[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;
}
}
#
#
#
支持的功能
我们的意图是 Rust trait 的所有功能都应该很好地与 #[async_trait] 一起工作,但边缘情况却很多。如果您看到意外的借用检查器错误、类型错误或警告,请提交一个 issue。展开代码中未使用 unsafe
,因此请放心,如果您的代码可以编译,那么它不太可能存在严重错误。
☑ 自身可以通过值、引用、可变引用或无 self 获取;
☑ 任何数量的参数,任何返回值;
☑ 泛型类型参数和生命周期参数;
☑ 关联类型;
☑ 在同一 trait 中有异步和非异步函数;
☑ 由 trait 提供的默认实现;
☑ 省略的生命周期;
☑ 支持 Dyn 的 trait。
说明
异步 fn 被转换为返回 Pin<Box<dyn Future + Send + 'async>>
的方法,并将委托给一个私有的异步独立函数。
例如,上面的 impl Advertisement for AutoplayingVideo
将展开为
impl Advertisement for AutoplayingVideo {
fn run<'async>(
&'async self,
) -> Pin<Box<dyn core::future::Future<Output = ()> + Send + 'async>>
where
Self: Sync + 'async,
{
async fn run(_self: &AutoplayingVideo) {
/* the original method body */
}
Box::pin(run(self))
}
}
非线程安全 futures
并非所有异步 trait 都需要 dyn Future + Send
的 futures。为了避免在异步 trait 方法上放置 Send 和 Sync 约束,请在 trait 和 impl 块上调用异步 trait 宏为 #[async_trait(?Send)]
。
省略的生命周期
请注意,异步 fn 语法不允许在 &
和 &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<'_>) {}
}
Dyn 特质
具有异步方法的特质可以作为 trait 对象使用,只要它们满足 dyn 的通常要求——没有类型参数的方法、没有通过值传递的 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 类型的 Future,async_trait 宏必须在接受 &self
的特性方法上发出一个 Sync 绑定,并在接受 &mut 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
方法,则 Sync)来约束特性的所有实现者,使得默认实现适用于它们
#
#[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;
依赖关系
~1.5MB
~35K SLoC