Sto tentando di implementare un enum "polimorfo" Input
che nasconde se stiamo leggendo da un file o da uno stdin. Più concretamente, sto provando a costruire un enum che avrà un metodo lines
che a sua volta "delegherà" quella chiamata a uno File
impacchettato in uno BufReader
oa uno StdInLock
(entrambi hanno il metodo lines()
).Come fare l'IO polimorfo (file o stdin) in Rust?
Ecco l'enum:
enum Input<'a> {
Console(std::io::StdinLock<'a>),
File(std::io::BufReader<std::fs::File>)
}
Ho tre metodi:
from_arg
per decidere se stiamo leggendo da un file o da uno stdin controllando se un argomento (nome del file) è stato a condizione,file
per avvolgere un file con unBufReader
,console
per Locki ng lo stdin.
L'implementazione:
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))
})
}
}
Per quanto ho capito, devo implementare bot BufRead
e Read
tratti per far funzionare tutto questo. Questo è il mio tentativo:
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()
}
}
}
Infine, l'invocazione:
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 */
}
}
Il problema è che il compilatore mi dice che io sono il pattern matching torto e che ho mismatched types
. Ho provato a soddisfarlo con:
match self {
Input::Console(std::io::StdinLock(ref c)) => c.lines(),
Input::File(std::io::BufReader(ref f)) => f.lines()
}
... ma anche questo non funziona.
Nel primo caso sto ottenendo l'errore 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(),
^~~~~~~~~
, e nel secondo caso un errore 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(),
Sono davvero fuori dalla mia profondità qui, sembra.
L'approccio corrente non funziona poiché 'StdinLock' contiene un riferimento a un oggetto' Stdin'. –
Potresti approfondire un po 'se hai tempo? Grazie. – neektza