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

Async trait methods with associated return type and dynamic dispatch

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

理想情况下我想:

我已经使用 #[async_trait] 尝试了类似下面的操作,但我无法编译它,主要是错误“无法将特征 Handler 制作成对象”。

是否有 better/more 惯用的方法来解决这个问题?

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 个问题。

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

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

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

但是,您不太可能希望在特征对象上调用 connect,因为它没有 self 参数。相反,您可以通过将 Self: Sized 约束添加到 connect.

来从特征对象中选择该特定函数

然后您可以创建特征对象,但 connect 将不可用。