2016-05-14 101 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 
     } 
    } 
}