繁体   English   中英

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

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

首先,对于具有默认实现的异步特征,它要求特征本身具有SendSync (取决于接收器类型)作为超级特征(在板条箱自述文件的“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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM