2017-02-15 4 views
0

Um mit Rust besser zu werden, habe ich beschlossen, einen einfachen Lexer zu implementieren, der einige Dokumente Zeile für Zeile analysiert.Typenkonflikt beim Klonen der Linien des Features BufRead

Da ich mindestens zweimal über die Linien des Merkmals iterieren BufRead, ich bin die Linien meiner BufRead Klonen, aber ich erhalte den folgenden Fehler:

error[E0271]: type mismatch resolving `<std::io::Lines<T> as std::iter::Iterator>::Item == &_` 
    --> <anon>:18:23 
    | 
18 |  let lines = lines.cloned(); 
    |      ^^^^^^ expected enum `std::result::Result`, found reference 
    | 
    = note: expected type `std::result::Result<std::string::String, std::io::Error>` 
    = note: found type `&_ 

error[E0271]: type mismatch resolving `<std::io::Lines<T> as std::iter::Iterator>::Item == &_` 

Ich verstehe, was der Fehler ist, aber basierend auf der following code, wie kann ich dem Compiler sagen, was die Item der Iterator sein sollte, so kann es den Typ richtig zu werfen?

use std::fmt::Write; 
use std::io::{BufRead, BufReader, Lines, Read}; 

pub struct DocumentMetadata { 
    language: String, 
    // ... 
} 

pub fn analyze<T: BufRead>(document: T) -> Result<DocumentMetadata,()> { 
    let lines = document.lines(); 
    let language = guess_language(&lines); 

    // Do more lexical analysis based on document language 

    Ok(DocumentMetadata { 
     language: language, 
     // ... 
    }) 
} 

fn guess_language<T: BufRead>(lines: &Lines<T>) -> String { 
    let lines = lines.cloned(); 
    for line in lines { 
     let line = line.unwrap(); 
     // Try to guess language 
    } 
    "en".to_string() 
} 

#[test] 
fn it_guesses_document_language() { 
    let mut document = String::new(); 
    writeln!(&mut document, "# language: en").unwrap(); 
    let document = BufReader::new(document.as_str().as_bytes()); 

    match analyze(document) { 
     Ok(metadata) => assert_eq!("en".to_string(), metadata.language), 
     Err(_) => panic!(), 
    } 
} 

Für Testzwecke Einheit, Ich baue einen Puffer mit einem String aber in einem normalen Gebrauch las ich es von einem File.

Antwort

2

Bewertung der Iterator::cloned Definition:

fn cloned<'a, T>(self) -> Cloned<Self> 
    where Self: Iterator<Item=&'a T>, 
      T: 'a + Clone 

Und die Umsetzung von Iterator für io::Lines:

impl<B: BufRead> Iterator for Lines<B> { 
    type Item = Result<String>; 
} 

Sie können nicht cloned verwenden, da das Iterator Element keine Referenz. Sie können den Compiler sonst nicht "erzählen"; So funktionieren Typen nicht.

As I have to iterate at least two times over the lines of the trait BufRead , I am cloning the lines of my BufRead

Das macht keinen Sinn. Klonen die Linien des Lesers würde nichts speichern. In der Tat würde es wahrscheinlich nur schlechter machen. Sie würden die Zeichenfolgen einmal erstellen, sie nicht verwenden, außer sie zu klonen, und sie dann ein drittes Mal erstellen, wenn Sie erneut iterieren.

Wenn Sie alle Saiten vermeiden wollen neu zu erstellen, collect alle Fäden in eine Vec oder andere Sammlung und dann durchlaufen, dass sie mehrmals:

pub fn analyze<T: BufRead>(document: T) -> Result<DocumentMetadata,()> { 
    let lines: Result<Vec<_>, _> = document.lines().collect(); 
    let lines = lines.unwrap(); 
    let language = guess_language(&lines); 

    // Do more lexical analysis based on document language 

    Ok(DocumentMetadata { 
     language: language, 
     // ... 
    }) 
} 

fn guess_language<'a, I>(lines: I) -> String 
    where I: IntoIterator<Item = &'a String>, 
{ 
    for line in lines { 
     // Try to guess language 
    } 
    "en".to_string() 
} 
+0

auf diese Weise mehr Sinn macht, in der Tat. Ich hatte die Idee, die Zeilen zu klonen, weil die meisten Methoden von 'io :: Lines 'den Iterator konsumieren und ich dachte nicht, dass' collect' an die Situation angepasst wäre. Vielen Dank! – Ianlet

Verwandte Themen