2016-05-14 102 views
6

我将一些Python移植到Rust作为学习练习,需要从文件或stdin中获取输入。我在结构中保留了我的输入的句柄,所以我认为我只是做了一个Box<io::Read>,但我遇到了需要查找输入的情况,并且seek不是Read特性的一部分。我知道你不能在管道中寻找,所以我会继续前进,并假设现在只有在输入是文件时才会调用此方法,但是我的问题是我无法在Rust中检查并向下转发。如果我无法寻找标准输入,我如何从标准输入或文件中获取输入?

我知道我可以使用两个输入类型的枚举,但它似乎应该有一个更优雅的方式来做到这一点。这就是我的问题,你如何做到这一点,而不是一团糟?

是否有可能将stdin或文件包装在同一种缓冲区中,以便我可以使用该类型而不用担心IO的类型?

+0

你需要什么操作来“查找”输入?如果你真的需要一个任意的“seek”,唯一的希望就是将整个stdin读入一个'Cursor >'。 – kennytm

+0

你显然不需要**来寻求你是否可以处理来自stdin的阅读。 – Shepmaster

回答

5

我知道,你说你想要的东西更优雅和不枚举,但我认为枚举的解决方案相当优雅。所以这里有一个尝试:

use std::fs; 
use std::io::{self, Read, Seek, SeekFrom}; 

enum Input { 
    File(fs::File), 
    Stdin(io::Stdin), 
} 

impl Read for Input { 
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { 
     match *self { 
      Input::File(ref mut file) => file.read(buf), 
      Input::Stdin(ref mut stdin) => stdin.read(buf), 
     } 
    } 
} 

impl Seek for Input { 
    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { 
     match *self { 
      Input::File(ref mut file) => file.seek(pos), 
      Input::Stdin(_) => { 
       Err(io::Error::new(
        io::ErrorKind::Other, 
        "not supported by stdin-input", 
       )) 
      }, 
     } 
    } 
} 

把这样的代码放在你的一些子模块中,不要再担心它了。您可以使用Input类型的对象,就像您使用File一样:无论如何,您必须处理查找错误,因此处理无法通过stdin查找应该非常容易。举例:

let arg = std::env::args().nth(1).unwrap(); 
let mut input = if arg == "--" { 
    Input::Stdin(io::stdin()) 
} else { 
    Input::File(fs::File::open(&arg).expect("I should handle that..")) 
}; 

let mut v = Vec::new(); 
let _idc = input.read_to_end(&mut v); 

match input.seek(SeekFrom::End(0)) { 
    Err(_) => println!("oh noes :("), 
    Ok(bytes) => println!("yeah, input is {} long", bytes), 
} 
2

是否可以将stdin或文件包装在同一种缓冲区中,以便我可以使用该类型而不用担心io的类型?

这正是特质Read所做的。看起来你想要的是StdinFile的抽象(特征),它具有对seek的可选支持,并允许查询有关此支持。在下面的代码,OptionalSeekRead特点就是用来实现这个打算:

use std::io::{Read, Seek, SeekFrom, Stdin}; 
use std::fs::File; 

// define a trait alias 
pub trait SeekRead: Seek + Read {} 

impl<T: Seek + Read> SeekRead for T {} 

pub trait OptionSeekRead: Read { 
    fn get_seek_read(&mut self) -> Option<&mut SeekRead>; 
} 

impl OptionSeekRead for File { 
    fn get_seek_read(&mut self) -> Option<&mut SeekRead> { 
     Some(self) 
    } 
} 

impl OptionSeekRead for Stdin { 
    fn get_seek_read(&mut self) -> Option<&mut SeekRead> { 
     None 
    } 
} 

struct Handle { 
    read: Box<OptionSeekRead>, 
} 

impl Handle { 
    fn f(&mut self) { 
     if let Some(h) = self.read.get_seek_read() { 
      // h is Seek + Read 
      h.seek(SeekFrom::Start(42)); 
     } else { 
      // without Seek 
     } 
    } 
}