`.map(f)` 和 `.map(|x| f(x))` 之间有什么区别?

时间:2021-06-07 01:09:12

标签: rust functional-programming iterator dereference trait-objects

在执行 rustlings standard_library_types/iterators2.rs 时,我开始想知道 std::iter::Iterator::map 如何调用它的参数闭包/函数。更具体地说,假设我有一个功能

// "hello" -> "Hello"
pub fn capitalize_first(input: &str) -> String {
    let mut c = input.chars();
    match c.next() {
        None => String::new(),
        Some(first) => String::from(first.to_ascii_uppercase()) + c.as_str(),
    }
}

现在我想用它

// Apply the `capitalize_first` function to a slice of string slices.
// Return a vector of strings.
// ["hello", "world"] -> ["Hello", "World"]
pub fn capitalize_words_vector(words: &[&str]) -> Vec<String> {
    words.into_iter().map(capitalize_first).collect()
}

无法编译

error[E0631]: type mismatch in function arguments
  --> exercises/standard_library_types/iterators2.rs:24:27
   |
11 | pub fn capitalize_first(input: &str) -> String {
   | ---------------------------------------------- found signature of `for<'r> fn(&'r str) -> _`
...
24 |     words.into_iter().map(capitalize_first).collect()
   |                           ^^^^^^^^^^^^^^^^ expected signature of `fn(&&str) -> _`

error[E0599]: the method `collect` exists for struct `Map<std::slice::Iter<'_, &str>, for<'r> fn(&'r str) -> String {capitalize_first}>`, but its trait bounds were not satisfied
  --> exercises/standard_library_types/iterators2.rs:24:45
   |
24 |       words.into_iter().map(capitalize_first).collect()
   |                                               ^^^^^^^ method cannot be called on `Map<std::slice::Iter<'_, &str>, for<'r> fn(&'r str) -> String {capitalize_first}>` due to unsatisfied trait bounds
   |
   = note: the following trait bounds were not satisfied:
           `<for<'r> fn(&'r str) -> String {capitalize_first} as FnOnce<(&&str,)>>::Output = _`
           which is required by `Map<std::slice::Iter<'_, &str>, for<'r> fn(&'r str) -> String {capitalize_first}>: Iterator`
           `for<'r> fn(&'r str) -> String {capitalize_first}: FnMut<(&&str,)>`
           which is required by `Map<std::slice::Iter<'_, &str>, for<'r> fn(&'r str) -> String {capitalize_first}>: Iterator`
           `Map<std::slice::Iter<'_, &str>, for<'r> fn(&'r str) -> String {capitalize_first}>: Iterator`
           which is required by `&mut Map<std::slice::Iter<'_, &str>, for<'r> fn(&'r str) -> String {capitalize_first}>: Iterator`

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0599, E0631.
For more information about an error, try `rustc --explain E0599`.

但是,在我将 .map(capitalize_first) 更改为 .map(|x| capitalize_first(x)) 后,它工作正常。显然,Rust 在将每个项目传递给 map 的参数闭包/函数之前借用(不确定是可变还是不可变),这是有道理的,因为我们通常不想消耗被迭代的对象。

我无法理解的是,为什么 Rust 不向 |x| capitalize_first(x) 借用参数。我假设闭包 |x| capitalize_first(x) 仍然得到 &&str,然后 auto-dereferencing rules 被踢进来并将它解引用到 &str,但这并不能解释为什么它没有踢在我使用函数 capitalize_first 时。 .map(|x| capitalize_first(x)).map(capitalize_first) 之间有什么区别?鉴于 map 的参数是一个 trait 对象,这里是否发生动态调度?

注意:这个问题与 Using a function with iter().map() - as a named function vs as a closure 不重复,因为我问的是为什么,而另一篇帖子问的是如何 .关于该问题的公认答案,如果有人能解释为什么我们需要 AsRef 而 Rust 已经拥有 auto-dereferencing rules,我将不胜感激。

2 个答案:

答案 0 :(得分:5)

为什么我可以使用 capitalize_first 参数调用 &&str

auto-dereferencing rules 的链接问答专门针对使用 self 语法时如何解析 a.b()。参数规则一般跳过自动引用步骤,只依赖于 Deref coercions。由于 &&str 实现了 Deref<Target = &str>(并且确实所有引用实现了 Deref),所以这个 &&str -> &str 转换是透明发生的。< /p>

为什么它不适用于 .map() 呢?

简单明了,map() 期待实现 Fn(&&str) -> Tcapitalize_first 没有的东西。 Fn(&str) 不会透明地转换为 Fn(&&str),它需要一个类似于闭包引入的转换步骤(尽管是透明的)。

答案 1 :(得分:1)

@kmdreko 是正确的,问题在于 deref 强制。请记住,&[T] 实现了 IntoIterator,其中 Self::Item&T。因此,如果 x : &[T],那么 x.into_iter() 将实现 Iterator,其中 Self::Item&T

就您而言,words.into_iter()Iterator,而 Self::Item&&str

map 方法需要一个域为 Self::Item 的函数。 words.into_iter().map(f) 期望 f 是一个输入类型为 &&str 的函数。

这会产生问题,因为 capitalize_first&str 而不是 &&str 作为其输入。因此,words.into_iter().map(capitalize_first) 无法进行类型检查。

然而,当我们查看 words.into_iter().map(|x| capitalize_first(x)) 时,神奇的事情发生了。闭包 |x| capitalize_first(x) 被神奇地强制转换为 |x| capitalize_first(*x)。然后进行类型检查。