如何合并std::str::lines和std::io::lines?
How to combine std::str::lines and std::io::lines?
我想写一个函数来解析文本,但文本可能来自外部文件或内部文件&str
。 parse
函数可能是这样的:
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
功能?
两个迭代器不产生相同的值:
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
and 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 section on where
clauses 以获取有关 特质界限 的更多信息。
在 parse
函数中使用 Borrow
绑定将允许您借用 &str
,但如果您需要 String
值,更好的方法是使用 Cow
.
使用 line.borrow().to_string()
获得 String
值将始终分配,即使当使用文件中的行调用 parse
时(在这种情况下,lines.map
产生 String
).
使用 line.into_owned()
将在使用 &str
中的行调用时进行分配,但在使用文件中的行调用时不会进行分配(只会展开传递给 [= 的 String
值24=]).
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());
}
我想写一个函数来解析文本,但文本可能来自外部文件或内部文件&str
。 parse
函数可能是这样的:
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
功能?
两个迭代器不产生相同的值:
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
and 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 section on where
clauses 以获取有关 特质界限 的更多信息。
在 parse
函数中使用 Borrow
绑定将允许您借用 &str
,但如果您需要 String
值,更好的方法是使用 Cow
.
使用 line.borrow().to_string()
获得 String
值将始终分配,即使当使用文件中的行调用 parse
时(在这种情况下,lines.map
产生 String
).
使用 line.into_owned()
将在使用 &str
中的行调用时进行分配,但在使用文件中的行调用时不会进行分配(只会展开传递给 [= 的 String
值24=]).
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());
}