如何在Rust中从文件或标准输入执行多态IO? [英] How to do polymorphic IO from either a File or stdin in Rust?

查看:146
本文介绍了如何在Rust中从文件或标准输入执行多态IO?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试实现一个多态" Input枚举,该枚举隐藏我们是从文件还是从标准输入中读取内容.更具体地说,我正在尝试构建一个具有lines方法的枚举,该方法将依次委托"调用包装为BufReaderStdInLockFile(两者都具有lines()方法.

I'm trying to implement a "polymorphic" Input enum which hides whether we're reading from a file or from a stdin. More concretely, I'm trying build an enum that will have a lines method that will in turn "delegate" that call to either a File wrapped into a BufReader or to a StdInLock (both of which have the lines() method).

这是枚举:

enum Input<'a> {
    Console(std::io::StdinLock<'a>),
    File(std::io::BufReader<std::fs::File>)
}

我有三种方法:

  • from_arg用于通过检查是否提供了参数(文件名)来确定是从文件读取还是从标准输入读取,
  • file用于使用BufReader包装文件,
  • console用于锁定标准输入.
  • from_arg for deciding whether we're reading from a file or from a stdin by checking whether an argument (filename) was provided,
  • file for wrapping a file with a BufReader,
  • console for locking the stdin.

实现:

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)),
        })
    }
}

据我了解,我必须同时实现BufReadRead特质才能正常工作.这是我的尝试:

As far as I understand, I have to implement both BufRead and Read traits for this to work. This is my attempt:

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 */ }
}

在操场上完成示例

编译器告诉我我的模式匹配错误,并且我有mismatched types:

The compiler tells me that I'm pattern matching wrongly and that I have 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<'_>>`

我试图通过以下方式满足它:

I tried to satisfy it with:

match self {
    Input::Console(std::io::StdinLock(ref c)) => c.lines(),
    Input::File(std::io::BufReader(ref f)) => f.lines(),
}

...但是那也不起作用.

... but that doesn't work either.

看来,我真的不在这里.

I'm really out of my depth here, it seems.

推荐答案

这是最简单的解决方案,但会借用并锁定Stdin.

This is the simplest solution but will borrow and lock Stdin.

use std::fs::File;
use std::io::{self, BufRead, Read};

struct Input<'a> {
    source: Box<BufRead + 'a>,
}

impl<'a> Input<'a> {
    fn console(stdin: &'a io::Stdin) -> Input<'a> {
        Input {
            source: Box::new(stdin.lock()),
        }
    }

    fn file(path: &str) -> io::Result<Input<'a>> {
        File::open(path).map(|file| Input {
            source: Box::new(io::BufReader::new(file)),
        })
    }
}

impl<'a> Read for Input<'a> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.source.read(buf)
    }
}

impl<'a> BufRead for Input<'a> {
    fn fill_buf(&mut self) -> io::Result<&[u8]> {
        self.source.fill_buf()
    }

    fn consume(&mut self, amt: usize) {
        self.source.consume(amt);
    }
}

由于使用默认特征方法,对于Input,完全实现了ReadBufRead.因此,您可以在Input上调用lines.

Due to default trait methods, Read and BufRead are fully implemented for Input. So you can call lines on Input.

let input = Input::file("foo.txt").unwrap();
for line in input.lines() {
    println!("input line: {:?}", line);
}

这篇关于如何在Rust中从文件或标准输入执行多态IO?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆