如何从 Rust 中的 File 或 stdin 进行多态 IO? [英] How to do polymorphic IO from either a File or stdin in Rust?

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

问题描述

我正在尝试实现一个多态"Input 枚举,它隐藏了我们是从文件读取还是从标准输入读取.更具体地说,我正在尝试构建一个具有 lines 方法的枚举,该方法将依次委托"调用包装到 BufReader 中的 FileStdInLock (两者都有 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<'_>>`

我试图满足它:

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

由于默认的 trait 方法,ReadBufRead 完全为 Input 实现.所以你可以在 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 中的 File 或 stdin 进行多态 IO?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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