热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

如何在特征中定义异步方法?

我有一个特征,我用它来抽象tokio::net::TcpStream和tokio::net::UnixStream:

我有一个特征,我用它来抽象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;
}
impl TryRead for TcpStream {
fn do_try_read(&self, buf: &mut [u8]) -> Result {
self.try_read(buf)
}
}

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

回答


目前,async fn不能用于特质。造成这种情况的原因有些复杂,但未来有计划取消此限制。你可以参考一下为什么traits中的async fn很难对问题进行更深入的分析。

关联类型

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

trait Readable {
type Output: Future>;
fn readable(&self) -> Self::Output;
}

具体的未来类型

实现此特征时,您可以使用任何实现 的类型Future,例如Ready来自标准库:

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

动态未来类型

async函数返回一个 opaque 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>>>; fn readable(&self) -> Self::Output {
let fut = async {
do_stuff().await
};
Box::pin(fut)
}
}

请注意,使用这些特征方法将导致每个函数调用的堆分配和动态分派。对于绝大多数应用程序来说,这不是一个很大的成本,但需要考虑。

捕获参考

可能出现的一个问题是关联类型Output没有生命周期,因此无法捕获任何引用:

struct Reader(String);
impl Readable for Reader {
type Output = Pin>>>; 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 捕获的盒装未来才能实现:

trait Readable {
// note the anonymous lifetime ('_) that refers to &self
fn readable(&self) -> Pin> + '_>>;
}
impl Readable for Reader {
fn readable(&self) -> Pin> + '_>> {
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-traitasync方法转换为返回的方法Pin + Send = '_>>,类似于我们之前写的,所以也应该考虑与上面相同的点。

为了避免Sendasynctrait 方法上放置绑定,您可以像#[async_trait(?Send)]在 trait 和 impl 块上一样调用异步 trait 宏。

不稳定的特性

如果你在夜间,故事会更好。您可以启用该type_alias_impl_trait功能并使用常规async/await语法而无需装箱:

#![feature(type_alias_impl_trait)]
trait Readable {
type Output: Future>;
fn readable(&self) -> Self::Output;
}
impl Readable for Reader {
type Output = impl Future>; fn readable(&self) -> Self::Output {
async { ... }
}
}

借用问题仍然适用于上述代码。但是,使用不稳定功能generic_associated_types,您可以Output在整个生命周期内进行泛型并捕获self

trait Readable {
type Output<'a>: Future>;
fn readable(&self) -> Self::Output<'_>;
}

前面的例子编译,零装箱!

struct Reader(String);
impl Readable for Reader {
type Output<'a> = impl Future> + 'a; fn readable(&self) -> Self::Output<'_> {
let fut = async move {
println!("{}", self.0); // we can capture self!
Ok(())
};
Box::pin(fut)
}
}






推荐阅读
  • C语言是计算机科学和编程领域的基石,许多初学者在学习过程中会感到困惑。本文将详细介绍C语言的基本概念、关键语法和实用示例,帮助你快速上手C语言。 ... [详细]
  • 本文介绍了Tomcat的基本操作,包括启动、关闭及首次访问的方法,并详细讲解了如何在IDEA中创建Web项目,配置Servlet及其映射,以及如何将项目部署到Tomcat。 ... [详细]
  • spring boot使用jetty无法启动 ... [详细]
  • 大华股份2013届校园招聘软件算法类试题D卷
    一、填空题(共17题,每题3分,总共51分)1.设有inta5,*b,**c,执行语句c&b,b&a后,**c的值为________答:5 ... [详细]
  • 我有一个从C项目编译的.o文件,该文件引用了名为init_static_pool ... [详细]
  • 本文详细介绍了MySQL数据库的基础语法与核心操作,涵盖从基础概念到具体应用的多个方面。首先,文章从基础知识入手,逐步深入到创建和修改数据表的操作。接着,详细讲解了如何进行数据的插入、更新与删除。在查询部分,不仅介绍了DISTINCT和LIMIT的使用方法,还探讨了排序、过滤和通配符的应用。此外,文章还涵盖了计算字段以及多种函数的使用,包括文本处理、日期和时间处理及数值处理等。通过这些内容,读者可以全面掌握MySQL数据库的核心操作技巧。 ... [详细]
  • 1、编写一个Java程序在屏幕上输出“你好!”。programmenameHelloworld.javapublicclassHelloworld{publicst ... [详细]
  • 深入解析C语言中的关键字及其分类
    本文将全面介绍C语言中的关键字,并按照功能将其分为数据类型关键字、控制结构关键字、存储类别关键字和其他关键字四大类,旨在帮助读者更好地理解和运用这些基本元素。C语言中共有32个关键字。 ... [详细]
  • 函子(Functor)是函数式编程中的一个重要概念,它不仅是一个特殊的容器,还提供了一种优雅的方式来处理值和函数。本文将详细介绍函子的基本概念及其在函数式编程中的应用,包括如何通过函子控制副作用、处理异常以及进行异步操作。 ... [详细]
  • 本文介绍了如何通过C#语言调用动态链接库(DLL)中的函数来实现IC卡的基本操作,包括初始化设备、设置密码模式、获取设备状态等,并详细展示了将TextBox中的数据写入IC卡的具体实现方法。 ... [详细]
  • 深入体验Python的高级交互式Shell - IPython
    IPython 是一个增强型的 Python 交互式 Shell,提供了比标准 Python 控制台更为强大的功能,适用于开发和调试过程。它不仅支持直接执行 Linux 命令,还提供了丰富的特性来提高编程效率。 ... [详细]
  • 本文详细介绍了如何在ARM架构的目标设备上部署SSH服务端,包括必要的软件包下载、交叉编译过程以及最终的服务配置与测试。适合嵌入式开发人员和系统集成工程师参考。 ... [详细]
  • 本文详细介绍了Windows网络编程中常用的几个关键结构体,包括sockaddr_in、in_addr和hostent,解释了它们的定义和用途,并提供了实际应用中的示例。 ... [详细]
  • 通过网上的资料我自己的实际内核编译,我把对Linux内核编译的过程写在这里,也许对其他的Linux爱好者的编译学习有些帮助,其中很大部分是 ... [详细]
  • mybatis 详解(七)一对一、一对多、多对多
    mybatis详解(七)------一 ... [详细]
author-avatar
爱娟一辈子-_709
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有