2016-05-04 3 views
3

Я хочу написать функцию для разбора текста, но текст может поступать из внешнего файла или внутреннего &str. parse функция может идти, как это:Как объединить std :: str :: lines и std :: io :: lines?

fn parse(lines: GenericLinesGenerator) { 
    for line in lines { 
     // parse content 
    } 
} 

... и он может быть вызван следующим образом:

use std::io::BufReader; 
use std::fs::File; 
let fin = BufReader::new(File::open("info.txt").expect("not found")); 
parse(TransformFromIO(fin.lines())) 

или

let content: &'static str = "some\nlong\ntext"; 
parse(TransformFromStr(content.lines())) 

Можно ли реализовать такую ​​функцию parse ?

ответ

4

Два итераторы не производят одни и те же значения:

impl<B: BufRead> Iterator for io::Lines<B> { 
    type Item = Result<String>; 
} 
impl<'a> Iterator for str::Lines<'a> { 
    type Item = &'a str; 
} 

Вы должны обрабатывать эту разницу как-то. Самое важное отличие состоит в том, что io::Lines может сбой. Ваша программа должна решить, как с этим бороться; Я решил просто прервать программу.

Следующее, что вам нужно сделать, это принять любой тип, который можно преобразовать в итератор, а значение, полученное от итератора, должно быть преобразовано в тип, с которым вы можете иметь дело. Похоже, что &str является общим знаменателем.

Это решается с помощью IntoIterator и Borrow:

use std::borrow::Borrow; 
use std::fs::File; 
use std::io::prelude::*; 
use std::io::BufReader; 

fn parse<I>(lines: I) 
    where I: IntoIterator, 
      I::Item: Borrow<str>, 
{ 
    for line in lines { 
     println!("line: {}", line.borrow()); 
    } 
} 

fn main() { 
    parse("alpha\nbeta\ngamma".lines()); 

    println!("----"); 

    let f = File::open("/etc/hosts").expect("Couldn't open"); 
    let b = BufReader::new(f); 
    parse(b.lines().map(|l| l.expect("Bad line!"))); 
} 

Регулярно проверяйте The Rust Programming Language раздел where clauses для получения дополнительной информации о черта ограничивает.

+0

Удивительный! Благодаря! – knight42

+0

Удивительно, что вам не нужно внедрять новые черты для такого рода проблем. – WiSaGaN

2

Borrow Использование связанного в функции parse позволит вам брать &str, но если вам нужно String ценности, лучший подход заключается в использовании Cow.

Использование line.borrow().to_string() для получения значения String всегда будет выделять, даже когда parse вызывается с линиями из файла (в данном случае, lines.map производит String).

Использование line.into_owned() выделит при вызове с линией из &str, но не будет выделять при вызове с линиями из файла (просто разворачивать String значения, передаваемое в Cow::Owned).

use std::borrow::Cow; 
use std::io::{BufReader, BufRead}; 
use std::iter::IntoIterator; 
use std::fs::File; 

fn parse<'a, I>(lines: I) 
    where I: IntoIterator, 
      I::Item: Into<Cow<'a, str>> 
{ 
    for line in lines { 
     let line: Cow<'a, str> = line.into(); 
     let line: String = line.into_owned(); 
     // or 
     let line = line.into().into_owned() 
     println!("{}", line); 
    } 
} 

fn main() { 
    let fin = BufReader::new(File::open("/etc/hosts").expect("cannot open file")); 
    parse(fin.lines().map(|r| r.expect("file read failed"))); 

    let content: &'static str = "some\nlong\ntext"; 
    parse(content.lines()); 
}