2016-03-18 19 views
6

Bence bir dosyadan veya bir girdiden okuma olmanıza gizleyen bir "polimorfik" Input enum uygulamaya çalışıyorum. Daha somut olarak, birya da BufReader ya da StdInLock (her ikisi de lines() yöntemine sahip olan) biryöntemine sahip olacak "temsilci" olacak bir enum inşa çalışıyorum. Ben üç yöntem varRust'ta polimorfik IO (Dosya veya stdin) nasıl yapılır?

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

: Burada

enum var biz bir argüman (dosya adı) olup olmadığını kontrol ederek bir dosyadan veya bir stdin'den okuyorsanız karar vermekten

  • from_arg oldu Locki için, bir BufReader içeren bir dosyayı sarılması için,
  • console
  • file
  • sağlanan Stdin.

uygulaması:

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

Bildiğim kadarıyla anladığım kadarıyla, çalışmak için bot bunun için BufRead ve Read özellikleri uygulamak zorunda.

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

Son olarak, çağırma:

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

sorun olduğunu ben desen yanlış eşleştirme ve ben mismatched types olduğunu olduğumu Banasöylemediğin derleyici Bu benim girişimidir. Bunu tatmin etmek için:

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

... ama bu da çalışmıyor. ,

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

Burada gerçekten benim derinlik yokum: Bir unresolved variant hata

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(), 
                 ^~~~~~~~~ 

ve ikinci durumda:

İlk durumda ben mismatched types hatası alıyorum görünüyor.

+0

'StdinLock' bir Stdin nesnesi için bir başvuru içerdiğinden mevcut yaklaşımınız çalışmayacaktır. –

+0

Zamanınız varsa biraz daha genişleyebilir misiniz? Teşekkürler. – neektza

cevap

9

Bu en basit çözümdür ancak ödünç ve Stdin kilitlenir. Nedeniyle varsayılan özellik yöntemlerine

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

, Read ve BufRead tamamen Input için uygulanmaktadır. Yani Input üzerinde lines çağırabilir.

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

Çok teşekkür ederim. – neektza

İlgili konular