首页 > 解决方案 > 使用带有 iter().map() 的函数 - 作为命名函数与作为闭包

问题描述

这两个片段来自 Python,在行为上几乎完全等效。它们都工作并提供相同的输出,尽管它们产生的字节码略有不同。

def lower_case(s):
    return s.lower()

map(lower_case, ["A", "B"])

对比

def lower_case(s):
    return s.lower()

map(lambda s: lower_case(s), ["A", "B"])

学习 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(),
    }
}

用另一个接受字符串向量的函数包装这个函数是有趣的地方:

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(),
    }
}

pub fn capitalize_words(words: Vec<&str>) -> Vec<String> {
    words.iter().map(|w| capitalize_first(w)).collect::<Vec<String>>()
}

这有效,但更换

words.iter().map(|w| capitalize_first(w)).collect::<Vec<String>>()

words.iter().map(capitalize_first).collect::<Vec<String>>()

导致编译失败并出现以下错误:

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

error[E0599]: no method named `collect` found for struct `std::iter::Map<std::slice::Iter<'_, &str>, for<'r> fn(&'r str) -> std::string::String {capitalize_first}>` in the current scope
   --> exercises/standard_library_types/iterators2.rs:27:40
    |
27  |     words.iter().map(capitalize_first).collect::<Vec<String>>()
    |                                        ^^^^^^^ method not found in `std::iter::Map<std::slice::Iter<'_, &str>, for<'r> fn(&'r str) -> std::string::String {capitalize_first}>`
    |
   ::: C:\Users\Adi\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib/rustlib/src/rust\src\libcore\iter\adapters\mod.rs:809:1
    |
809 | pub struct Map<I, F> {
    | -------------------- doesn't satisfy `_: std::iter::Iterator`
    |
    = note: the method `collect` exists but the following trait bounds were not satisfied:
            `<for<'r> fn(&'r str) -> std::string::String {capitalize_first} as std::ops::FnOnce<(&&str,)>>::Output = _`
            which is required by `std::iter::Map<std::slice::Iter<'_, &str>, for<'r> fn(&'r str) -> std::string::String {capitalize_first}>: std::iter::Iterator`
            `for<'r> fn(&'r str) -> std::string::String {capitalize_first}: std::ops::FnMut<(&&str,)>`
            which is required by `std::iter::Map<std::slice::Iter<'_, &str>, for<'r> fn(&'r str) -> std::string::String {capitalize_first}>: std::iter::Iterator`
            `std::iter::Map<std::slice::Iter<'_, &str>, for<'r> fn(&'r str) -> std::string::String {capitalize_first}>: std::iter::Iterator`
            which is required by `&mut std::iter::Map<std::slice::Iter<'_, &str>, for<'r> fn(&'r str) -> std::string::String {capitalize_first}>: std::iter::Iterator`

我相信我明白。

但是,按照建议进行更改

capitalize_first(input: &str)

capitalize_first(input: &&str)

通过编译,但现在测试失败(显然,因为用,而不是capitalize_first调用):&str&&str

error[E0308]: mismatched types                                               
  --> exercises/standard_library_types/iterators2.rs:40:37                   
   |                                                                         
40 |         assert_eq!(capitalize_first("hello"), "Hello");                 
   |                                     ^^^^^^^ expected `&str`, found `str`
   |                                                                         
   = note: expected reference `&&str`                                        
              found reference `&'static str`                                 
                                                                             
error[E0308]: mismatched types                                               
  --> exercises/standard_library_types/iterators2.rs:45:37                   
   |                                                                         
45 |         assert_eq!(capitalize_first(""), "");                           
   |                                     ^^ expected `&str`, found `str`     
   |                                                                         
   = note: expected reference `&&str`                                        
              found reference `&'static str`                                 

是否有任何妥协可以允许words.iter().map(capitalize_first).collect::<Vec<String>>()工作,同时仍然允许现有的测试capitalize_first通过?

map(capitalize_first)和之间的区别map(|x| capitalize_first(x))可能是可以忽略的(在视觉上、语法上和性能上),但是定义一个接受参数的闭包只是为了调用具有相同参数的函数是非常烦人的(有些人甚至会说这是一种反模式)。

标签: rust

解决方案


您可以更改capitalize_first为使用具有AsRef特征的泛型:

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

这将使它与&str&&strString以及任何数量的嵌套引用str)兼容。


推荐阅读