我认为我对生命周期有一个很好的理解,但是我所阅读的内容并没有与编译器在闭包时说的内容相符;)
我有一个带签名的功能:
fn split_to_words(content: &str) -> Vec<String>
使用for
循环,它看起来像这样:
let mut acc: Vec<String> = Vec::new();
for line in content.lines() {
if line.is_empty() { continue };
for word in line.split_whitespace() {
acc.push(word.to_lowercase());
}
}
并使用迭代器:
let acc: Vec<String> = content.lines()
.filter(|x| !x.is_empty())
.map(|x| x.to_lowercase())
.flat_map(|x: String| x.split_whitespace())
.map(|x| x.to_string())
.collect();
但最终出现了错误:
error: `x` does not live long enough
.flat_map(|x: String| x.split_whitespace())
^
答案 0 :(得分:3)
您的迭代器样式代码与命令式代码不完全相同:在命令式代码中,您在split_whitespace
之前执行to_lowercase
,而在迭代器代码中,您执行相反的操作。事实证明,之前执行split_whitespace
效率更高,因为split_whitespace
不需要分配Strings
;它只返回给定字符串切片中的切片。另一方面,to_lowercase
需要分配一个新字符串,所以通过最后一次,我们可以保存一个分配。
let acc: Vec<String> = content.lines()
.filter(|x| !x.is_empty())
.flat_map(|x| x.split_whitespace())
.map(|x| x.to_lowercase())
.collect();
答案 1 :(得分:2)
查看整个错误消息很有帮助:
error: `x` does not live long enough
note: reference must be valid for the method call at ...
note: ...but borrowed value is only valid for the scope
of parameters for function at ...
调用flat_map
后,将String
的所有权传递给关闭。但是,闭包尝试返回包含对String
的引用的迭代器。由于每个String
将在闭包调用结束后被释放,因此对它的引用将无效,因此编译器会阻止您执行此操作。
我可能会使用for
- 循环变体或类似版本:
fn split_to_words(content: &str) -> Vec<String> {
let lines = content.lines()
.filter(|x| !x.is_empty())
.map(|x| x.to_lowercase());
let mut result = Vec::new();
for line in lines {
for word in line.split_whitespace() {
result.push(word.to_string());
}
}
result
}
fn main() -> () {
println!("{:?}", split_to_words("hello world"));
}
在这个的特殊情况下,我认为你可以把它写成:
fn split_to_words(content: &str) -> Vec<String> {
content.split_whitespace().map(str::to_lowercase).collect()
}
换行符是空格,因此无需使用lines
。这也消除了空白值的可能性。