我试图实现一个"多态" Input
枚举,它隐藏了我们是从文件还是从stdin读取。更具体地说,我试图构建一个枚举,它将具有一个lines
方法,该方法将依次"委托"调用 Package 到BufReader
中的File
或StdInLock
(两者都具有lines()
方法)。
下面是枚举:
enum Input<'a> {
Console(std::io::StdinLock<'a>),
File(std::io::BufReader<std::fs::File>)
}
我有三种方法:
from_arg
,用于通过检查是否提供了参数(文件名)来决定是从文件还是从标准输入中读取,file
用于用BufReader
Package 文件,console
用于锁定标准输入。
实施:
impl<'a> Input<'a> {
fn console() -> Input<'a> {
Input::Console(io::stdin().lock())
}
fn file(path: String) -> io::Result<Input<'a>> {
match File::open(path) {
Ok(file) => Ok(Input::File(std::io::BufReader::new(file))),
Err(_) => panic!("kita"),
}
}
fn from_arg(arg: Option<String>) -> io::Result<Input<'a>> {
Ok(match arg {
None => Input::console(),
Some(path) => try!(Input::file(path)),
})
}
}
据我所知,我必须同时实现BufRead
和Read
traits才能工作。
impl<'a> io::Read for Input<'a> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match *self {
Input::Console(ref mut c) => c.read(buf),
Input::File(ref mut f) => f.read(buf),
}
}
}
impl<'a> io::BufRead for Input<'a> {
fn lines(self) -> Lines<Self> {
match self {
Input::Console(ref c) => c.lines(),
Input::File(ref f) => f.lines(),
}
}
fn consume(&mut self, amt: usize) {
match *self {
Input::Console(ref mut c) => c.consume(amt),
Input::File(ref mut f) => f.consume(amt),
}
}
fn fill_buf(&mut self) -> io::Result<&[u8]> {
match *self {
Input::Console(ref mut c) => c.fill_buf(),
Input::File(ref mut f) => f.fill_buf(),
}
}
}
最后,调用:
fn load_input<'a>() -> io::Result<Input<'a>> {
Ok(try!(Input::from_arg(env::args().skip(1).next())))
}
fn main() {
let mut input = match load_input() {
Ok(input) => input,
Err(error) => panic!("Failed: {}", error),
};
for line in input.lines() { /* do stuff */ }
}
Complete example in the playground
编译器告诉我模式匹配错误,我有mismatched types
:
error[E0308]: match arms have incompatible types
--> src/main.rs:41:9
|
41 | / match self {
42 | | Input::Console(ref c) => c.lines(),
| | --------- match arm with an incompatible type
43 | | Input::File(ref f) => f.lines(),
44 | | }
| |_________^ expected enum `Input`, found struct `std::io::StdinLock`
|
= note: expected type `std::io::Lines<Input<'a>>`
found type `std::io::Lines<std::io::StdinLock<'_>>`
我试图满足它:
match self {
Input::Console(std::io::StdinLock(ref c)) => c.lines(),
Input::File(std::io::BufReader(ref f)) => f.lines(),
}
......但那也没用。
我真的超出了我的深度在这里,它似乎。
3条答案
按热度按时间8fsztsew1#
@A.B.的答案是正确的,但是它试图符合OP的原始程序结构。我希望为偶然发现这个问题的新手(就像我一样)提供一个更具可读性的替代方案。
请参见reddit中的讨论,我从该讨论中借用了代码。
注意boxed BufRead前面的
dyn
关键字,这种模式称为trait object。lyr7nygr2#
这是最简单的解决方案,但会借用并锁定
Stdin
。由于默认的trait方法,
Input
完全实现了Read
和BufRead
,所以你可以在Input
上调用lines
。ibps3vxo3#
如果你愿意稍微调整一下代码的结构,你实际上可以不用动态调度,你只需要确保读取器使用的代码被 Package 在它自己的函数中,并且在编译时知道该函数的参数的具体类型。
因此,如果我们暂时避开
enum Input
的想法,并以@Yerke的答案为基础,我们可以做到:因为每次调用
output_lines
时,R
都有一个具体的类型,所以编译器可以将output_lines
函数单态化,并进行静态调度。在我看来,除了代码不那么复杂(不需要Box
Package )之外,它还稍微快一些,编译器可以进行更多的优化。