使用iter.map,为什么闭包有效,但直接传递函数却无效?

时间:2019-11-17 03:04:34

标签: rust iterator closures

我在Rust中有此函数,该函数将字符串大写。

pub fn capitalize_first(input: &str) -> String {
    let mut c = input.chars();
    match c.next() {
        None => String::new(),
        Some(first) => first.to_uppercase().collect::<String>() + c.as_str(),
    }
}

稍后,我用它来遍历字符串向量。

let words = vec!["hello", "world"];
let capitalized_words: Vec<String> =
    words.iter().map(|word| capitalize_first(word)).collect();

这可以按预期工作,但是我注意到闭包|word| capitalize_first(word)毫无用处。因此,我尝试通过直接传递capitalize_first来代替它。

let words = vec!["hello", "world"];
let capitalized_words: Vec<String> = words.iter().map(capitalize_first).collect();

但是,它无法通过以下错误消息进行编译。

10 | pub fn capitalize_first(input: &str) -> String {
   | ---------------------------------------------- found signature of `for<'r> fn(&'r str) -> _`
...
38 |         let capitalized_words: Vec<String> = words.iter().map(capitalize_first).collect();
   |                                                               ^^^^^^^^^^^^^^^^ expected signature of `fn(&&str) -> _`

我无法理解此错误。为什么闭包有效,但直接传递函数无效。有什么我可以更改的东西,可以让我传递函数引用而不是进行无用的关闭吗?

1 个答案:

答案 0 :(得分:5)

当您像调用words.iter()那样遍历集合时,就遍历了对元素的引用。向量中的元素类型为&str,因此对元素的引用类型为&&str

因此map期望一个函数接受类型为&&str的参数,这就是推断闭包中的word参数的方式。然后,当您在capitalize_first上调用word时,由于对所有引用都实现了Deref特性,它会自动取消引用到&str

但是,即使由于此转换您的函数似乎接受&&str类型的参数,该转换也会在函数外部进行。因此,这并不意味着您可以传递函数来代替期望&&str的函数。

有2个解决方案。您可以将函数更改为更通用的函数,并使其接受实现AsRef<str>的任何东西。然后它将接受可以解除引用到str的任何内容,包括&str&&strString等。

pub fn capitalize_first<S: AsRef<str>>(input: S) -> String {
    let mut c = input.as_ref().chars();
    // Don't forget this ^

    match c.next() {
        None => String::new(),
        Some(first) => first.to_uppercase().collect::<String>() + c.as_str(),
    }
}

或者您可以保持其功能不变,并通过在迭代器上调用copied()来在调用站点处对其进行修复,这实际上将取消对元素的引用。

let capitalized_words: Vec<String> = words.iter().copied().map(capitalize_first).collect();

我建议第一种方法。