具有关联的返回类型和动态调度的异步特征方法



我需要处理来自几个不同来源的异步数据流,这些数据流都应该被解析为某种规范化的格式。

理想情况下,我想:

  • 为每个实现某些Handler特性的源编写一个处理程序,以便下游调用方可以与数据流交互,并且对底层实现是不可知的。

  • 有一些函数可以在给定密钥的情况下返回Box<dyn Handler>,因为实现Handler的源数量有限/可预测。

我使用#[async_trait]尝试过类似以下的操作,但我无法将其编译,主要是出现错误";特征CCD_ 5不能被制成对象";。

有没有更好/更惯用的方法来解决这个问题?

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;
}

这里有两个问题。

首先,对于具有默认实现的异步特征,它要求特征本身具有SendSync(取决于接收器类型(作为超特征(在机箱自述的"dyn-traits"部分有一个简短的解释:https://docs.rs/async-trait/latest/async_trait/)

其次,connect函数不是对象安全的,因为它的返回类型是关联函数。这与异步无关,这是一个对象安全问题。这个简化的例子有同样的问题:

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!()
}
}

但是,您不太可能希望在trait对象上调用connect,因为它没有self参数。相反,您可以通过向connect添加Self: Sized约束来从trait对象中选择该特定函数。

然后可以创建一个trait对象,但connect不可用。

最新更新