【问题标题】:How to combine std::str::lines and std::io::lines?如何结合 std::str::lines 和 std::io::lines?
【发布时间】:2016-08-29 22:27:15
【问题描述】:

我想编写一个函数来解析文本,但文本可能来自外部文件或内部&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函数?

【问题讨论】:

    标签: generics iterator rust traits


    【解决方案1】:

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

    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 可能失败。你的程序必须决定如何处理它;我选择中止程序。

    接下来您需要做的是接受任何可以转换为迭代器的类型,并且从迭代器产生的值必须转换为您可以处理的类型。看来&amp;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!")));
    }
    

    查看where clauses 上的The Rust Programming Language 部分,了解有关特征界限的更多信息。

    【讨论】:

    • 令人惊讶的是,您不需要为此类问题实现任何新特征。
    【解决方案2】:

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

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

    使用line.into_owned() 将在使用来自&amp;str 的行调用时进行分配,但在使用来自文件的行调用时不会进行分配(只会解开传递给Cow::OwnedString 值)。

    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());
    }
    

    【讨论】:

      猜你喜欢
      • 2019-01-07
      • 2019-10-26
      • 1970-01-01
      • 2014-04-26
      • 2012-01-29
      • 2012-10-27
      • 2017-03-14
      • 2013-11-16
      • 1970-01-01
      相关资源
      最近更新 更多