起步
最近在看《Command Line Rust》。读到第四章,根据书上要求挑战按字节读取文件内容,于是写下 reader.take(num_bytes as u64)
,结果 nvim 不乐意了,提示:the take
method cannot be invoked on a trait object。
一番搜索没找到答案,求助配套代码,结果不但没有找出不同,反而更迷惑了:我的代码始终编译出错,样例代码却正常运行。
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 提问吧。很容易得到答案。
由于
BufRead
是Read
的超集,实现了BufRead
的类型自动也就实现了Read
。这意味着您在处理Box<dyn BufRead>
实例时,可以通过转换为&mut dyn Read
或Box<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);
还不快抢沙发