Why isn't this closure the same as function?

I'm working on the rustlings iterators2.rs exercise. Just for completeness, I will put my full working solution at the end, but my question focuses on just a couple lines.

I have the below which works

words.iter().map(|word| capitalize_first(word)).collect()

But since the closure is just forwarding a single parameter to the function, I want to understand why this is not equivalent to

words.iter().map(capitalize_first).collect()

which fails to compile.

My full Solution

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

// Step 2.
// 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 {
words.iter().map(|word| capitalize_first(word)).collect()
}

// Step 3.
// Apply the capitalize_first function again to a slice of string slices.
// Return a single string.
// ["hello", " ", "world"] -> "Hello World"
pub fn capitalize_words_string(words: &[&str]) -> String {
words.iter().map(|word| capitalize_first(word)).collect()
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_success() {
    assert_eq!(capitalize_first("hello"), "Hello");
}

#[test]
fn test_empty() {
    assert_eq!(capitalize_first(""), "");
}

#[test]
fn test_iterate_string_vec() {
    let words = vec!["hello", "world"];
    assert_eq!(capitalize_words_vector(&words), ["Hello", "World"]);
}

#[test]
fn test_iterate_into_string() {
    let words = vec!["hello", " ", "world"];
    assert_eq!(capitalize_words_string(&words), "Hello World");
}

}

Hint: what's the type of word?

2 Likes

words.iter() is a borrowing iterator, producing references to the elements of words. So its items have type &&str.

Rust will coerce &&str to &str in expressions like capitalize_first(word), which is why the closure version works.

However, it will not do fancier coercions like fn(&str) -> String into fn(&&str) -> String, which would be needed to make .map(capitalize_first) work.

However, you can use .copied() to create an iterator that yields &str:

words.iter().copied().map(capitalize_first).collect()
4 Likes

Thank you, that makes sense.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.