2016-05-14 47 views
6

Przenoszę niektóre Python do Rust jako ćwiczenie uczenia się i trzeba wprowadzić dane wejściowe z pliku lub stdin. Zachowuję uchwyt do mojego wejścia w strukturze, więc pomyślałem, że zrobię tylko Box<io::Read>, ale natrafiłem na sytuację, w której muszę szukać na wejściu, a seek nie jest częścią cechy Read. Wiem, że nie możesz szukać w rurach, więc idę do przodu i zakładam, że ta metoda jest wywoływana tylko wtedy, gdy dane wejściowe są plikiem, ale moim problemem jest to, że nie mogę sprawdzić tego i zgubić w Rust.Jak mogę pobrać dane wejściowe ze stdin lub pliku, jeśli nie mogę przeszukiwać stdin?

Wiem, że mogę użyć wyliczenia dla dwóch typów danych wejściowych, ale wydaje się, że powinien być bardziej elegancki sposób na to. I to jest moje pytanie, jak to zrobić i nie robić bałaganu?

Czy można owinąć stdin lub plik w tym samym buforze, więc mógłbym po prostu użyć tego typu i nie martwić się o typ IO?

+0

Jakie operacje są potrzebne, aby "wyszukać" dane wejściowe? Jeśli naprawdę potrzebujesz arbitralnego 'seek' jedyną nadzieją jest przeczytanie całego stdin na' Cursor >. – kennytm

+0

Oczywiście nie musisz ** szukać, jeśli możesz obsłużyć czytanie ze standardowego wejścia. – Shepmaster

Odpowiedz

5

wiem, że chcesz coś bardziej elegancki i bez teksty stałe, ale myślę, że enum-rozwiązanie jest dość elegancki. Więc tutaj jest jedna próba:

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

kod Put tak w jakiś moduł sub waszej i nie martw się o tym zbyt dużo więcej. Możesz użyć obiektu typu Input tak, jakbyś użył File: musisz mimo wszystko obsługiwać błędy wyszukiwania, więc obsługa niemożności szukania przez stdin powinna być bardzo łatwa. Przykład:

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

Czy można owinąć stdin lub plik w tym samym buforze, więc mógłbym po prostu użyć tego typu i nie martwić się o typ io?

To właśnie robi ta cecha Read. Wygląda na to, że to, co chcesz, to abstrakcja (cecha) dla Stdin i File, która ma opcjonalne wsparcie dla seek i pozwala zapytać o to wsparcie. W poniższym kodzie OptionalSeekRead cecha jest wykorzystywana do realizacji tego zamiaru:

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