从行迭代器创建单词迭代器

时间:2018-12-03 19:13:56

标签: rust iterator borrow-checker

我有一个字符串迭代器thread_ts,它是从stdin中获得的

curl https://slack.com/api/chat.postMessage -X POST -H 'Content-type: application/json; charset=utf-8' -H "Authorization: Bearer TOKEN" --data '{"channel":"CHANNEL-ID", "text":"curl", "thread_ts":"THREAD-TS"}'

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

还有其他干净的方法可以做到这一点吗?

1 个答案:

答案 0 :(得分:1)

在示例代码中,lines是对从stdin获得的读取器读取的行进行迭代的迭代器。如您所说,它将返回String个实例,但您没有将它们存储在任何地方。

std::string::String::split_whitespace的定义如下:

pub fn split_whitespace(&self) -> SplitWhitespace

因此,它引用了一个字符串-它不使用该字符串。它返回一个迭代器,该迭代器生成字符串切片&str-引用字符串的一部分,但不拥有它。

实际上,一旦您完成传递给flat_map的闭包,没有人拥有它,因此将其删除。这将使&str产生的words悬空,从而产生错误。

一种解决方案是将这些行收集到一个向量中,如下所示:

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>中,该实例可以继续存在,以便&str产生的words可以引用。

如果有很多行,并且您不想将它们全部保留在内存中,则您可能希望一次执行一行:

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>并将每个单词复制到其中的开销。

您可能一直希望零拷贝实现,它消耗了Strings产生的lines。我认为可以通过创建一个split_whitespace()函数来创建String所有权并返回拥有该字符串的迭代器来创建该函数。