2017-02-15 4 views
0

Pour aller mieux avec Rust, j'ai décidé d'implémenter un lexer simple qui analyse certains documents ligne par ligne.Type discordance lors du clonage des lignes du trait BufRead

Comme je l'ai itérer au moins deux fois sur les lignes du trait BufRead, je suis le clonage des lignes de mon BufRead mais je reçois l'erreur suivante:

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 == &_` 

Je comprends ce que l'erreur est, mais basé sur le following code, comment puis-je dire au compilateur ce que le Item du Iterator devrait être ainsi il peut mouler correctement le type?

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

Pour des fins de tests unitaires, je construis un tampon avec un String mais dans un usage normal je l'ai lu d'un File.

Répondre

2

Revoir la définition Iterator::cloned:

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

Et la mise en œuvre de Iterator pour io::Lines:

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

Vous ne pouvez pas utiliser cloned parce que l'élément iterator n'est pas une référence. Vous ne pouvez pas "dire" le compilateur autrement; ce n'est pas comment les types fonctionnent.

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

Cela n'a pas vraiment de sens. Cloner les lignes du lecteur ne sauverait rien. En fait, il serait probablement juste faire des choses pire. Vous créez les chaînes une fois, ne les utilisez pas, sauf pour les cloner, puis les créez une troisième fois lorsque vous recommencez à les parcourir.

Si vous souhaitez éviter de recréer toutes les chaînes, collect toutes les chaînes dans une Vec ou une autre collection, puis itérer que plusieurs fois:

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

Cela fait beaucoup plus de sens, en effet. J'ai été bloqué sur l'idée de cloner les lignes car la plupart des méthodes de 'io :: Lines 'consomment l'itérateur et je ne pensais pas que' collect' était adapté à la situation. Merci! – Ianlet