如何组合std :: str :: lines和std :: io :: lines?

时间:2016-05-04 12:55:27

标签: generics iterator rust traits

我想编写一个解析文本的函数,但文本可能来自外部文件或内部&strparse函数可能如下所示:

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功能?

2 个答案:

答案 0 :(得分:5)

两个迭代器不会产生相同的值:

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是共同点。

使用IntoIteratorBorrow

解决了这个问题
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部分,了解有关特征界限的更多信息。

答案 1 :(得分:2)

使用Borrow函数中绑定的parse将允许您借用&str,但如果您需要String值,则更好的方法是使用{{1} }}

使用Cow获取line.borrow().to_string()值将始终分配,即使使用文件中的行调用String(在这种情况下,parse生成lines.map })。

当使用来自String的行调用时,使用line.into_owned()将进行分配,但是当使用来自文件的行调用时将不会分配(将仅释放传递给&str的{​​{1}}值})。

String