【问题标题】:Using a function with iter().map() - as a named function vs as a closure使用带有 iter().map() 的函数 - 作为命名函数与作为闭包
【发布时间】:2020-07-20 23:18:26
【问题描述】:

这两个 sn-ps 来自 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 是用&amp;str 调用的,而不是&amp;&amp;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::&lt;Vec&lt;String&gt;&gt;() 工作,同时仍然允许capitalize_first 的现有测试通过?

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

【问题讨论】:

    标签: rust


    【解决方案1】:

    您可以更改 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(),
        }
    }
    

    这将使它与&amp;str&amp;&amp;str(和String 以及对str 的任意数量的嵌套引用)兼容。

    【讨论】:

      猜你喜欢
      • 2018-10-10
      • 1970-01-01
      • 1970-01-01
      • 2021-12-30
      • 1970-01-01
      • 2018-12-16
      • 2015-12-12
      • 2015-07-03
      • 1970-01-01
      相关资源
      最近更新 更多