[英]Async trait methods with associated return type and dynamic dispatch
我需要处理来自几个不同来源的异步数据流,这些数据流都应该被解析成某种规范化格式。
理想情况下,我想:
为实现某些Handler
特征的每个源编写一个处理程序,以便下游调用者可以与数据流交互,并且与底层实现无关。
有一些 function 可以返回Box<dyn Handler>
给定一个键,因为实现Handler
的源数量有限/可预测。
我已经尝试使用#[async_trait]
进行类似以下操作,但我无法编译它,主要是出现错误“无法将特征Handler
程序制成对象”。
有没有更好/更惯用的方法来解决这个问题?
use async_trait; // 0.1.52
use tokio; // 1.15.0
#[async_trait::async_trait]
trait Handler{
type Output;
async fn connect()->Self::Output;
async fn read_parse(&self)->Vec<i32>;
async fn run(&self) {
for _ in 0..5 {
self.read_parse().await;
}
}
}
struct FooHandler;
#[async_trait::async_trait]
impl Handler for FooHandler {
type Output = FooHandler;
async fn connect() -> Self::Output {
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
FooHandler{}
}
async fn read_parse(&self)->Vec<i32> {
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
vec![1, 2, 3]
}
}
struct BarHandler;
#[async_trait::async_trait]
impl Handler for BarHandler {
type Output = BarHandler;
async fn connect() -> Self::Output {
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
BarHandler{}
}
async fn read_parse(&self)->Vec<i32> {
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
vec![1, 2, 3]
}
}
async fn get_handler(name:&str)->Option<Box<dyn Handler>> {
match name {
"FOO"=>Some(Box::new(FooHandler::connect().await)),
"BAR"=>Some(Box::new(BarHandler::connect().await)),
_=>None
}
}
#[tokio::main]
async fn main() {
let handler = get_handler("FOO").await.unwrap();
handler.run().await;
}
编辑:
根据 cameron1024 的回答,我能够使以下工作:
use async_trait; // 0.1.52
use tokio; // 1.15.0
#[async_trait::async_trait]
trait Handler: Sync {
async fn connect()->Self
where
Self: Sized;
async fn read_parse(&self)->Vec<i32>;
async fn run(&self) {
for _ in 0..5 {
let res = self.read_parse().await;
println!("{:?}", res);
}
}
}
struct FooHandler;
#[async_trait::async_trait]
impl Handler for FooHandler {
async fn connect() -> Self {
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
println!("connected");
FooHandler{}
}
async fn read_parse(&self)->Vec<i32> {
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
vec![1, 2, 3]
}
}
async fn get_handler(name: &str)->Option<Box<dyn Handler>> {
match name {
"FOO"=>Some(Box::new(FooHandler::connect().await)),
_=>None
}
}
#[tokio::main]
async fn main() {
// let handler = FooHandler::connect().await;
let handler = get_handler("FOO").await.unwrap();
handler.run().await;
}
这里有2个问题。
首先,对于具有默认实现的异步特征,它要求特征本身具有Send
或Sync
(取决于接收器类型)作为超级特征(在板条箱自述文件的“dyn 特征”部分中有简要说明: https://docs.rs/async-trait/latest/async_trait/ )
其次, connect
function 不是 object 安全的,因为它的返回类型是关联的 function。 这与异步无关,这是一个 object 安全问题。 这个简化的例子有同样的问题:
fn main() {
let x: Box<dyn Foo<Bar = ()>> = Box::new(());
}
trait Foo {
type Bar;
fn connect() -> Self::Bar;
}
impl Foo for () {
type Bar = ();
fn new() -> Self::Bar {
todo!()
}
}
但是,您不太可能希望在特征 object 上调用connect
,因为它没有self
参数。 相反,您可以通过将Self: Sized
约束添加到connect
来选择特定的 function 离开特征 object 。
然后您可以创建一个特征 object,但connect
将不可用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.