从行迭代器创建单词迭代器
Creating word iterator from line iterator
我有一个字符串迭代器lines
,我从标准输入
use std::io::{self, BufRead};
let mut stdin = io::stdin();
let lines = stdin.lock().lines().map(|l| l.unwrap());
lines
迭代器产生 String
类型的值,而不是 &str
。我想创建一个迭代器来迭代输入的单词而不是行。看起来这应该是可行的,但我天真的尝试不起作用:
let words = lines.flat_map(|l| l.split_whitespace());
编译器告诉我 l
在被借用的同时被删除,这是有道理的:
error[E0597]: `l` does not live long enough
--> src/lib.rs:6:36
|
6 | let words = lines.flat_map(|l| l.split_whitespace());
| ^ - `l` dropped here while still borrowed
| |
| borrowed value does not live long enough
7 | }
| - borrowed value needs to live until here
是否有其他干净的方法可以完成此操作?
在您的示例代码中,lines
是对您从 stdin
获得的 reader 中读入的行的迭代器。正如你所说,它 returns String
个实例,但你没有将它们存储在任何地方。
std::string::String::split_whitespace
定义如下:
pub fn split_whitespace(&self) -> SplitWhitespace
因此,它引用了一个字符串——它不使用该字符串。它 returns 一个产生字符串切片的迭代器 &str
- 它引用字符串的一部分,但不拥有它。
事实上,一旦你传递给 flat_map
的闭包用完了,就没有人拥有它,所以它被丢弃了。这将使 words
产生的 &str
悬而未决,因此是错误。
一种解决方案是将线收集到向量中,如下所示:
let lines: Vec<String> = stdin.lock().lines().map(|l| l.unwrap()).collect();
let words = lines.iter().flat_map(|l| l.split_whitespace());
String
个实例保存在 Vec<String>
中,它可以继续存在,以便 words
产生的 &str
有参考价值。
如果有很多行,并且您不想将它们全部保存在内存中,您可能更愿意一次一行地执行:
let lines = stdin.lock().lines().map(|l| l.unwrap());
let words = lines.flat_map(|l| {
l.split_whitespace()
.map(|s| s.to_owned())
.collect::<Vec<String>>()
.into_iter()
});
这里把每一行的词汇集成一个Vec
,一次一行。权衡是减少整体内存消耗,而不是为每行构建 Vec<String>
并将每个单词复制到其中的开销。
您可能一直希望零拷贝实现,它消耗了 lines
产生的 Strings
。我认为可以通过创建一个 split_whitespace()
函数来创建一个拥有 String
和 returns 拥有字符串的迭代器。
我有一个字符串迭代器lines
,我从标准输入
use std::io::{self, BufRead};
let mut stdin = io::stdin();
let lines = stdin.lock().lines().map(|l| l.unwrap());
lines
迭代器产生 String
类型的值,而不是 &str
。我想创建一个迭代器来迭代输入的单词而不是行。看起来这应该是可行的,但我天真的尝试不起作用:
let words = lines.flat_map(|l| l.split_whitespace());
编译器告诉我 l
在被借用的同时被删除,这是有道理的:
error[E0597]: `l` does not live long enough
--> src/lib.rs:6:36
|
6 | let words = lines.flat_map(|l| l.split_whitespace());
| ^ - `l` dropped here while still borrowed
| |
| borrowed value does not live long enough
7 | }
| - borrowed value needs to live until here
是否有其他干净的方法可以完成此操作?
在您的示例代码中,lines
是对您从 stdin
获得的 reader 中读入的行的迭代器。正如你所说,它 returns String
个实例,但你没有将它们存储在任何地方。
std::string::String::split_whitespace
定义如下:
pub fn split_whitespace(&self) -> SplitWhitespace
因此,它引用了一个字符串——它不使用该字符串。它 returns 一个产生字符串切片的迭代器 &str
- 它引用字符串的一部分,但不拥有它。
事实上,一旦你传递给 flat_map
的闭包用完了,就没有人拥有它,所以它被丢弃了。这将使 words
产生的 &str
悬而未决,因此是错误。
一种解决方案是将线收集到向量中,如下所示:
let lines: Vec<String> = stdin.lock().lines().map(|l| l.unwrap()).collect();
let words = lines.iter().flat_map(|l| l.split_whitespace());
String
个实例保存在 Vec<String>
中,它可以继续存在,以便 words
产生的 &str
有参考价值。
如果有很多行,并且您不想将它们全部保存在内存中,您可能更愿意一次一行地执行:
let lines = stdin.lock().lines().map(|l| l.unwrap());
let words = lines.flat_map(|l| {
l.split_whitespace()
.map(|s| s.to_owned())
.collect::<Vec<String>>()
.into_iter()
});
这里把每一行的词汇集成一个Vec
,一次一行。权衡是减少整体内存消耗,而不是为每行构建 Vec<String>
并将每个单词复制到其中的开销。
您可能一直希望零拷贝实现,它消耗了 lines
产生的 Strings
。我认为可以通过创建一个 split_whitespace()
函数来创建一个拥有 String
和 returns 拥有字符串的迭代器。