2016-05-14 18 views
6

Ich portiere Python als Lernübung nach Rust und muss Eingaben entweder von einer Datei oder von einer Standardeingabe nehmen. Ich behalte einen Griff zu meiner Eingabe in einer Struktur, so dass ich dachte, ich würde nur eine Box<io::Read> machen, aber ich lief in eine Situation, wo ich auf der Eingabe suchen muss, und seek ist nicht Teil der Read Eigenschaft. Ich weiß, dass Sie nicht in Pipes suchen können, also gehe ich voraus und gehe davon aus, dass diese Methode nur aufgerufen wird, wenn die Eingabe eine Datei ist, aber mein Problem ist, dass ich das in Rust nicht überprüfen und ablehnen kann.Wie kann ich Eingaben von stdin oder einer Datei nehmen, wenn ich nicht nach stdin suchen kann?

Ich weiß, dass ich eine Enum für die zwei Eingabearten verwenden könnte, aber es scheint, dass es eine elegantere Möglichkeit geben sollte, dies zu tun. Und das ist meine Frage, wie machst du das und machst keine Unordnung?

Ist es möglich, Stdin oder eine Datei in die gleiche Art von Puffer zu wickeln, so dass ich nur diesen Typ verwenden und nicht über die Art der IO kümmern?

+0

Welche Operationen benötigen Sie, um die Eingabe zu "suchen"? Wenn Sie wirklich eine willkürliche 'Suche' brauchen, dann lesen Sie die ganze Standardeingabe in einen' Cursor > '. – kennytm

+0

Sie brauchen offensichtlich ** nicht ** zu suchen, wenn Sie mit dem Lesen von stdin umgehen können. – Shepmaster

Antwort

5

Ich weiß, Sie sagen, Sie etwas elegante und ohne Aufzählungen, aber ich denke, die ENUM-Lösung ist ziemlich elegant wollen. Also hier ist ein Versuch:

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

Put-Code wie das in einigen Submodul von Ihnen und mach dir keine Sorgen mehr darüber zu viel. Sie können ein Objekt vom Typ Input genauso verwenden, wie Sie eine File verwenden würden: Sie müssen ohnehin mit Suchfehlern umgehen, daher sollte die Handhabung der Unfähigkeit, nach stdin zu suchen, super einfach sein. Ein Beispiel:

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

Ist es möglich, Stdin oder eine Datei in die gleiche Art von Puffer zu wickeln, so dass ich nur diesen Typ verwenden und nicht über die Art von Io kümmern?

Das ist genau das, was das Merkmal Read tut. Es scheint, dass was Sie wollen, ist eine Abstraktion (Merkmal) für Stdin und File, die optionale Unterstützung für seek hat und ermöglicht, über diese Unterstützung abzufragen. Im folgenden Code wird OptionalSeekRead Merkmal verwendet, um dies zu erfüllen beabsichtigt:

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