尝试使用dyn AsyncReadExt,“特征无法变成对象”错误

问题描述

我正在使用旨在与Tcpstream或stdin / stdout一起使用的网络服务。我收到一个编译错误the trait tokio::io::util::async_read_ext::AsyncReadExt cannot be made into an object。目前,我的解决方法是使用包装枚举:

enum ClientReader {
    Stream(OwnedReadHalf),Stdin(Stdin),}

enum ClientWriter {
    Stream(OwnedWriteHalf),Stdout(Stdout),}

这需要在整个地方使用“匹配”块,这看起来不太优雅。

我做了一个简化的项目来解决这个问题:

Cargo.toml

[package]
name = "demo"
version = "0.1.0"
authors = ["test"]
edition = "2018"

[dependencies]
tokio = { version = "0.2",features = ["full"] }

src / main.rs

use tokio::io::AsyncReadExt;

struct Test {
  test: Box<dyn AsyncReadExt>,}

fn main () {
}

这会产生类似的错误

error[E0038]: the trait `tokio::io::AsyncReadExt` cannot be made into an object
    --> src/main.rs:4:3
     |
4    |   test: Box<dyn AsyncReadExt>,|   ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `tokio::io::AsyncReadExt` cannot be made into an object
     |
    ::: /home/???/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.22/src/io/util/async_read_ext.rs:162:12
     |
162  |         fn read<'a>(&'a mut self,buf: &'a mut [u8]) -> Read<'a,Self>
     |            ---- the trait cannot be made into an object because method `read` references the `Self` type in its return type
...
280  |         fn read_exact<'a>(&'a mut self,buf: &'a mut [u8]) -> ReadExact<'a,Self>
     |            ---------- the trait cannot be made into an object because method `read_exact` references the `Self` type in its return type

...

我不确定如何继续。我当时正在考虑为枚举包装程序使用一个巨大的impl块,但这似乎比match块还要耗费更多精力。在OO语言中,会有一个父类或接口,所以我研究了trait_enum板条箱以自动生成包装器隐含内容,但要使其工作起来会遇到很多麻烦。

目前,我确定唯一可以进行的清理工作就是将变通方法移至宏或函数中。

如果有任何更好的方法,我将不胜感激。 :)

编辑:根据user4815162342的建议,我对类型T进行了泛型构造:AsyncReadExt,这似乎适用于我的示例。稍后将尝试我的较大项目。

use tokio::io::AsyncReadExt;

struct Test<T: AsyncReadExt> {
  test: T,}

async fn myfn<T: AsyncReadExt> (mut t: Test<T>) where T: std::marker::Unpin {
    let mut v = Vec::<u8>::new();
    t.test.read_buf(&mut v).await;
    ()
}

fn main () {

}

解决方法

要将 AsyncRead 转换为 trait 对象,您应该使用类型 Pin<Box<dyn AsyncRead>>

use std::pin::Pin;
use tokio::io::AsyncRead;

struct Test {
    test: Pin<Box<dyn AsyncRead>>,}

impl Test {
    fn new<T: AsyncRead>(io: T) -> Self {
        Self {
            test: Box::pin(io),}
    }
}

AsyncReadExt trait 是一个扩展 trait,当提到 AsyncRead 的类型时,你应该总是使用 AsyncRead trait。