如何在特征中定义异步方法? [英] How can I define an async method in a trait?

查看:39
本文介绍了如何在特征中定义异步方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个特性,我用它来抽象 tokio::net::TcpStreamtokio::net::UnixStream:

I have a trait that I'm using to abstract away tokio::net::TcpStream and tokio::net::UnixStream:

/// Interface for TcpStream and UnixStream.
trait TryRead {
  // overlapping the name makes it hard to work with
  fn do_try_read(&self, buf: &mut [u8]) -> Result<usize, std::io::Error>;
}

impl TryRead for TcpStream {
  fn do_try_read(&self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
      self.try_read(buf)
  }
}

问题是我想抽象掉pub async fn readable(&self) ->io::Result<()> 在这两种方法中,但异步方法不能在特征中实现.我该如何处理?

The problem is that I want to abstract away pub async fn readable(&self) -> io::Result<()> in both methods but async methods cannot be implemented in traits. How can I handle this?

推荐答案

目前,async fn 不能在 Trait 中使用.造成这种情况的原因有些复杂,但未来有计划取消此限制.你可以参考为什么特征中的异步 fn 很难深入分析问题.

Currently, async fn cannot be used in traits. The reasons for this are somewhat complex, but there are plans to remove this restriction in the future. You can refer to why async fn in traits are hard for a deeper analysis of the problem.

同时,您可以使用关联类型:

In the meantime, you can use an associated type:

trait Readable {
    type Output: Future<Output = io::Result<()>>;

    fn readable(&self) -> Self::Output;
}

在实现这个特性时,你可以使用任何实现了 Future 的类型,比如 futures crate 中的 Ready:

When implementing this trait, you can use any type that implements Future, such as Ready from the futures crate:

impl Readable for Reader {
    type Output = future::Ready<io::Result<()>>;
    
    fn readable(&self) -> Self::Output {
        future::ready(Ok(()))
    }
}

如果您无法静态输入结果,例如您正在调用 async 函数,您可以返回一个拥有的动态类型的 Future:

In cases where you can't statically type your result, such as if you are calling an async function, you can return an owned dynamically typed Future:

impl Readable for Reader {
    type Output = Pin<Box<dyn Future<Output = io::Result<()>>>>;
    
    fn readable(&self) -> Self::Output {
        let fut = async {
            do_stuff().await
        };
        Box::pin(fut)
    }
}

请注意,使用这些 trait 方法将导致每个函数调用的堆分配和动态分派.对于绝大多数应用程序来说,这并不是一笔很大的开销,但在决定是否使用此功能时应予以考虑.

Note that using these trait methods will result in a heap allocation and dynamic dispatch per-function-call. This is not a significant cost for the vast majority of applications, but should be considered when deciding whether to use this functionality.

为了避免这些样板文件,您可以使用 async-trait 箱子:

To avoid some of this boilerplate, you can use the async-trait crate:

#[async_trait]
trait Readable {
    fn async readable(&self) -> io::Result<()>;
}

#[async_trait]
impl Readable for Reader {
    async fn readable(&self) -> io::Result<()> {
        do_stuff().await
    }
}

async-traitasync 方法转换为返回 Pin<Box<dyn Future<Output = ...> 的方法.+ Send>>,所以上面同样的点也应该考虑.

async-trait transforms async methods into methods that return Pin<Box<dyn Future<Output = ...> + Send>>, so the same above points should be considered as well.

为了避免将 Send 绑定放在 async trait 方法上,您可以调用异步 trait 宏作为 #[async_trait(?Send)] 在 trait 和 impl 块上.

To avoid having Send bound placed on the async trait methods, you can invoke the async trait macro as #[async_trait(?Send)] on both the trait and the impl blocks.

这篇关于如何在特征中定义异步方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆