【问题标题】:What's the differenece between `.map(f)` and `.map(|x| f(x))`?`.maf)` 和 `.map(|x| f(x))` 有什么区别?
【发布时间】:2021-08-24 03:24:14
【问题描述】:

在做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) 仍然得到&amp;&amp;str,然后auto-dereferencing rules 启动并将其取消引用到&amp;str,但这并不能解释为什么当我使用该函数时它没有启动capitalize_first.map(|x| capitalize_first(x)).map(capitalize_first) 有什么区别?考虑到map 的参数是一个特征对象,这里是否发生了动态调度?

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

【问题讨论】:

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


    【解决方案1】:

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

    auto-dereferencing rules 的链接问答专门针对使用 a.b() 语法时如何解析 self。参数规则通常会跳过自动引用步骤,只依赖Deref coercions。由于&amp;&amp;str 实现了Deref&lt;Target = &amp;str&gt;(实际上所有 引用都实现了Deref),因此&amp;&amp;str -> &amp;str 转换是透明地发生的。

    那为什么 .map() 就不行了呢?

    简单地说,map() 期望实现 Fn(&amp;&amp;str) -&gt; Tcapitalize_first 没有的东西。 Fn(&amp;str) 不会透明地转换为 Fn(&amp;&amp;str),它需要像闭包引入的转换步骤(尽管是透明的)。

    【讨论】:

    • (实际上所有引用都实现了 Deref) 我希望您能对此进行扩展:&amp;T 是否始终实现 Deref&lt;Target = T&gt;,以便我始终可以通过 @987654340 @ 到 f(x: T)?文件似乎没有提到这一点,所以我最好要求澄清一下。
    • @nalzok 不,deref 强制仅适用于 &amp;T&amp;U 的转换;两者都必须是参考。它专为&amp;Vec&lt;T&gt;&amp;[T]&amp;String&amp;str&amp;Box&lt;T&gt;&amp;T 等情况而设计。
    • 我明白了,但是&amp;&amp;str -> &amp;str 怎么适合这张照片?一般来说,我们有&amp;…&amp;&amp;T -> &amp;…&amp;T?
    【解决方案2】:

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

    在您的情况下,words.into_iter()IteratorSelf::Item&amp;&amp;str

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

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

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

    【讨论】:

    猜你喜欢
    • 2015-08-24
    • 2011-05-10
    • 2012-08-09
    • 2016-07-23
    • 2020-05-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-18
    相关资源
    最近更新 更多