gpt4 book ai didi

rust - 如何在 trait 中定义异步方法?

转载 作者:行者123 更新时间:2023-12-03 11:24:32 26 4
gpt4 key购买 nike

我有一个用来抽象的特征 tokio::net::TcpStreamtokio::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<()>在这两种方法中,但异步方法不能在特征中实现。我该如何处理?

最佳答案

目前,async fn不能用于特征。造成这种情况的原因有些复杂,但有计划在 future 取消此限制。您可以引用why async fn in traits are hard以便对问题进行更深入的分析。
关联类型
同时,您可以使用关联类型:

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

fn readable(&self) -> Self::Output;
}
具体的 future 类型
在实现这个 trait 时,你可以使用任何实现了 Future 的类型。 ,如 Ready来自标准库:
use std::future;

impl Readable for Reader {
type Output = future::Ready<io::Result<()>>;

fn readable(&self) -> Self::Output {
future::ready(Ok(()))
}
}
动态 future 类型 async函数返回一个不透明的 impl Future ,所以如果你需要调用一个,你没有具体的类型来设置 Output到。相反,您可以返回动态类型的 Future :
impl Readable for Reader {
// or use the handy type alias from the futures crate:
// futures::BoxFuture<'static, io::Result<()>>
type Output = Pin<Box<dyn Future<Output = io::Result<()>>>>;

fn readable(&self) -> Self::Output {
let fut = async {
do_stuff().await
};
Box::pin(fut)
}
}
请注意,使用这些 trait 方法将导致每个函数调用的堆分配和动态调度。对于绝大多数应用程序来说,这不是一个显着的成本,但需要考虑。
捕获引用
可能出现的一个问题是关联类型 Output没有生命周期,因此无法捕获任何引用:
struct Reader(String);

impl Readable for Reader {
type Output = Pin<Box<dyn Future<Output = io::Result<()>>>>;

fn readable(&self) -> Self::Output {
let fut = async move {
println!("{}", self.0);
Ok(())
};
Box::pin(fut)
}
}
error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
--> src/lib.rs:17:30
|
16 | fn readable(&self) -> Self::Output {
| ----- this data with an anonymous lifetime `'_`...
17 | let fut = async move {
| ______________________________^
18 | | println!("{}", self.0);
19 | | Ok(())
20 | | };
| |_________^ ...is captured here...
21 | Box::pin(fut)
| ------------- ...and is required to live as long as `'static` here
稳定 Rust 上的关联类型不能有生命周期,因此您必须将输出限制为从 self 捕获的盒装 future ,以使这成为可能:
trait Readable {
// note the anonymous lifetime ('_) that refers to &self
fn readable(&self) -> Pin<Box<dyn Future<Output = io::Result<()>> + '_>>;
}

impl Readable for Reader {
fn readable(&self) -> Pin<Box<dyn Future<Output = io::Result<()>> + '_>> {
let fut = async move {
println!("{}", self.0);
Ok(())
};
Box::pin(fut)
}
}
async_trait为避免某些样板文件,您可以使用 async-trait 箱:
#[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-trait变换 async方法转换为返回 Pin<Box<dyn Future<Output = ...> + Send = '_>> 的方法,与我们之前写的类似,因此也应考虑与上述相同的点。
为了避免有 Send绑定(bind)放置在 async trait 方法,您可以调用异步 trait 宏为 #[async_trait(?Send)]在 trait 和 impl block 上。
不稳定的特征
如果你在夜间,故事会更好。您可以启用 type_alias_impl_trait功能和使用常规 async/await没有装箱的语法:
#![feature(type_alias_impl_trait)]

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

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


impl Readable for Reader {
type Output = impl Future<Output = io::Result<()>>;

fn readable(&self) -> Self::Output {
async { ... }
}
}
借用问题仍然适用于上述代码。但是,使用不稳定的功能 generic_associated_types , 你使 Output终身通用并捕获 self :
trait Readable {
type Output<'a>: Future<Output = io::Result<()>>;

fn readable(&self) -> Self::Output<'_>;
}
并且前面的示例编译,零装箱!
struct Reader(String);

impl Readable for Reader {
type Output<'a> = impl Future<Output = io::Result<()>> + 'a;

fn readable(&self) -> Self::Output<'_> {
let fut = async move {
println!("{}", self.0); // we can capture self!
Ok(())
};
Box::pin(fut)
}
}

关于rust - 如何在 trait 中定义异步方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65921581/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com