Rust中map / flat_map中变量的生命周期

时间:2016-05-01 14:22:08

标签: rust

我认为我对生命周期有一个很好的理解,但是我所阅读的内容并没有与编译器在闭包时说的内容相符;)

我有一个带签名的功能:

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

2 个答案:

答案 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。这也消除了空白值的可能性。