2016-03-18 57 views
6

Próbuję zaimplementować wyliczenie "polimorficzne" Input, które ukrywa, czy czytamy z pliku lub ze standardowego wejścia. Dokładniej, próbuję zbudować wyliczenie, które będzie miało metodę lines, która z kolei "przekaże" to wywołanie do File zapakowanego w BufReader lub do StdInLock (oba z nich mają metodę lines()).Jak zrobić polimorficzne IO (plik lub stdin) w Rust?

Oto wyliczenia:

enum Input<'a> { 
    Console(std::io::StdinLock<'a>), 
    File(std::io::BufReader<std::fs::File>) 
} 

Mam trzy metody:

  • from_arg celu podjęcia decyzji, czy mamy do odczytu z pliku lub ze standardowego wejścia, sprawdzając, czy argument (filename) był pod warunkiem,
  • file owijania pliku z BufReader,
  • console do locki Na stdin.

Realizacja:

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

O ile mi zrozumieć, muszę wdrożyć BOT BufRead i Read cechy dla tej pracy. To moja próba:

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

Wreszcie inwokacja:

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

Problem polega na tym, że kompilator I mówi mi, że jestem dopasowując wzór niewłaściwie i że mam mismatched types. Próbowałem go zaspokoić:

match self { 
    Input::Console(std::io::StdinLock(ref c)) => c.lines(), 
    Input::File(std::io::BufReader(ref f)) => f.lines() 
} 

... ale to też nie działa.

W pierwszym przypadku Dostaję błąd mismatched types:

poly_input.rs:43:40: 43:49 error: mismatched types: 
expected `std::io::Lines<Input<'a>>`, 
    found `std::io::Lines<std::io::stdio::StdinLock<'_>>` 
(expected enum `Input`, 
    found struct `std::io::stdio::StdinLock`) [E0308] 
poly_input.rs:43    Input::Console(ref c) => c.lines(), 
                 ^~~~~~~~~ 

, a w drugim przypadku jest unresolved variant błąd:

poly_input.rs:45:29: 45:47 error: unresolved enum variant, struct or const `StdinLock` [E0419] 
poly_input.rs:45    Input::Console(std::io::StdinLock(ref c))   => c.lines(), 

Jestem naprawdę z moim głębokość tutaj wydaje się.

+0

Twoje obecne podejście nie zadziała, ponieważ 'StdinLock' zawiera odniesienie do obiektu' Stdin'. –

+0

Czy mógłbyś rozwinąć trochę, jeśli masz czas? Dzięki. – neektza

Odpowiedz

9

To jest najprostsze rozwiązanie, ale pożyczyć i zablokować Stdin.

use std::io::{self, Read, BufRead}; 
use std::fs::File; 

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

z powodu braku metod cecha, Read i BufRead są w pełni realizowane za Input. Możesz więc zadzwonić pod numer lines pod adresem Input.

let input = Input::file("foo.txt").unwrap(); 
for line in input.lines() { 
    println!("input line: {:?}", line); 
} 
+0

Dziękuję bardzo. – neektza