2016-03-18 13 views
6

Tôi đang cố gắng triển khai một "đa hình" Input enum để ẩn chúng ta đang đọc từ một tệp hay từ một stdin. Cụ thể hơn, tôi đang cố gắng xây dựng một enum sẽ có phương thức lines sẽ lần lượt là "đại biểu" gọi tới số File được bao bọc thành BufReader hoặc đến StdInLock (cả hai đều có phương thức lines()).Làm thế nào để làm IO đa hình (File hoặc stdin) trong Rust?

Đây là enum:

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

tôi có ba phương pháp:

  • from_arg cho việc quyết định xem chúng ta đang đọc từ một tập tin hoặc từ một thiết bị nhập chuẩn bằng cách kiểm tra xem liệu một cuộc tranh cãi (filename) là được cung cấp,
  • file để gói một tệp với BufReader,
  • console cho locki ng stdin.

Việc thực hiện:

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

Theo như tôi hiểu, tôi phải thực hiện bot BufReadRead đặc điểm này để làm việc. Đây là nỗ lực của tôi:

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

Cuối cùng, gọi:

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

Vấn đề là trình biên dịch tôi nói với tôi rằng tôi là mô hình kết hợp sai và rằng tôi có mismatched types. Tôi đã cố thỏa mãn điều đó với:

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

... nhưng điều đó cũng không có tác dụng.

Trong trường hợp đầu tiên tôi nhận được lỗi 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(), 
                 ^~~~~~~~~ 

, và trong trường hợp thứ hai một lỗi unresolved variant:

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

Tôi thực sự ra khỏi sâu của tôi ở đây, dường như.

+0

Cách tiếp cận hiện tại của bạn sẽ không hoạt động vì 'StdinLock' chứa tham chiếu đến đối tượng' Stdin'. –

+0

Bạn có thể mở rộng một chút nếu bạn có thời gian không? Cảm ơn. – neektza

Trả lời

9

Đây là giải pháp đơn giản nhất nhưng sẽ mượn và khóa 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); 
    } 
} 

Do phương pháp đặc điểm mặc định, ReadBufRead được thực hiện đầy đủ cho Input. Vì vậy, bạn có thể gọi số lines trên Input.

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

Cảm ơn bạn rất nhiều. – neektza

Các vấn đề liên quan