the `xxx` method cannot be invoked on a trait object

Rust 2023-08-30 2890 字 931 浏览 点赞

起步

最近在看《Command Line Rust》。读到第四章,根据书上要求挑战按字节读取文件内容,于是写下 reader.take(num_bytes as u64) ,结果 nvim 不乐意了,提示:the take method cannot be invoked on a trait object

image-20230830223320221

一番搜索没找到答案,求助配套代码,结果不但没有找出不同,反而更迷惑了:我的代码始终编译出错,样例代码却正常运行。

BufRead trait 与 Read trait

删除不相关代码,程序大概长下面这样:

type MyResult<T> = Result<T, Box<dyn std::error::Error>>;

pub fn run(config: Config) -> MyResult<()> {
    for filename in config.files {
        match open(&filename) {
            Err(e) => eprintln!("failed to open: {}", e),
            Ok(mut reader) => {
                if let Some(num_bytes) = config.bytes {
                    let mut buf = vec![0; num_bytes];
                    let mut handle = reader.take(5);  // the `take` method cannot be invoked on a trait object
                }
            }
        }
    }
    Ok(())
}

fn open(filename: &str) -> MyResult<Box<dyn BufRead>> {
    match filename {
        "-" => Ok(Box::new(BufReader::new(io::stdin()))),
        _ => Ok(Box::new(BufReader::new(File::open(filename)?))),
    }
}

跳进源码,可以看到 take 方法是在 Read trait 中实现的:

pub trait Read {
    // ... ignore others
    fn take(self, limit: u64) -> Take<Self>
    where
        Self: Sized,
    {
        Take { inner: self, limit }
    }
}

再看 BufRead:

pub trait BufRead: Read {
    // ... ignore others
}

也就是说,BufRead trait 是 Read trait 的超集,作为 Box<dyn BufRead> 的具体实例,一旦实现了 BufRead,就必然实现了 Read,可为什么会调不了 take() 呢?

chatgpt 如是说...

既然摸清楚 BufRead 和 Read 的关系,那我们大胆向 chatgpt 提问吧。很容易得到答案。

由于 BufReadRead 的超集,实现了 BufRead 的类型自动也就实现了 Read。这意味着您在处理 Box<dyn BufRead> 实例时,可以通过转换为 &mut dyn ReadBox<dyn Read> 来调用 take 方法。

如果您在代码中添加了 use std::io::Read;,那么您可以在当前作用域内使用 Read trait 中定义的方法,包括 take 方法。

即,第一种方式,将实例 Box\<dyn Read\> 类型转换为 &mut dyn Read:

let mut buf = vec![0, 5];

let mut reader: Box<dyn BufRead> = ...
let reader: &mut dyn Read = &mut reader;
let mut handle = reader.take(5);  // ok
let _ = handle.read(&mut buf);

第二种方式,将实例包装成 Box\<dyn Read\>。

let mut buf = vec![0, 5];

let reader: Box<dyn BufRead> = ...
let mut reader: Box<dyn Read> = Box::new(reader);
let mut handle = reader.take(5);  // ok
let _ = handle.read(&mut buf);

第三种,也最简单,不用转换类型,只要在作用于中引入 Read trait 就可以啦。

use std::io::Read;

let mut buf = vec![0, 5];

let reader: Box<dyn BufRead> = ...
let mut handle = reader.take(5);  // ok
let _ = handle.read(&mut buf);


本文由 Guan 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

还不快抢沙发

添加新评论