I've got this function in Rust that capitalizes a string.
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(),
}
}
Later, I use it to iterate over a vector of strings.
let words = vec!["hello", "world"];
let capitalized_words: Vec<String> =
words.iter().map(|word| capitalize_first(word)).collect();
This works as expected, but I notice that the closure |word| capitalize_first(word) is pretty useless. So I tried to replace it with passing capitalize_first directly like this.
let words = vec!["hello", "world"];
let capitalized_words: Vec<String> = words.iter().map(capitalize_first).collect();
This, however, fails to compile with the following error message.
10 | pub fn capitalize_first(input: &str) -> String {
| ---------------------------------------------- found signature of `for<'r> fn(&'r str) -> _`
...
38 | let capitalized_words: Vec<String> = words.iter().map(capitalize_first).collect();
| ^^^^^^^^^^^^^^^^ expected signature of `fn(&&str) -> _`
I'm having trouble understanding this error. Why does the closure work but passing the function directly does not. Is there something I can change that would allow me to pass the function reference instead of making a useless closure?
When you iterate over a collection like you are with your call to words.iter(), you are iterating over references to your elements. The elements in your vector are of type &str, and so references to your elements are of type &&str.
So map is expecting a function which takes an argument of type &&str, and so that is what the word argument in your closure is inferred as. Then when you call capitalize_first on word, it is automatically dereferenced to &str, due to the Deref trait being implemented for all references.
However, even though your function will appear to accept arguments of type &&str due to this conversion, the conversion happens outside your function. So it doesn't mean that your function can be passed in place of a function which expects &&str.
There are 2 solutions. You can either change your function to be more generic, and have it accept anything that implements AsRef<str>. Then it will accept anything that can be dereferenced to str, which includes &str, &&str and String, among others.
pub fn capitalize_first<S: AsRef<str>>(input: S) -> String {
let mut c = input.as_ref().chars();
// Don't forget this ^
match c.next() {
None => String::new(),
Some(first) => first.to_uppercase().collect::<String>() + c.as_str(),
}
}
Or you can keep your function as it is, and fix it at the call site, by calling copied() on your iterator, which will essentially dereference the elements.
let capitalized_words: Vec<String> = words.iter().copied().map(capitalize_first).collect();
I would recommend the first approach.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With