In my attempt to make my code a little bit cleaner, I tried passing the closure directly by name to the map()
to an iterator for a &[&str]
. map will need to iterate over &&str
which is understandable but my closure only takes &str
.
Code example:
fn capitalize_first(word: &str) -> String {
word.chars().next().unwrap().to_uppercase().to_string()
}
fn main() {
let cap_cap = |word: &&str| -> String {
word.chars().next().unwrap().to_uppercase().to_string()
};
let a = ["we", "are", "testing"];
let b = &a; // this is where this becomes interesting.
b.iter().map(|&x| capitalize_first(x)); // Works
b.iter().map(|x| capitalize_first(x)); // Works
b.iter().map(cap_cap); // That's what the compiler expects.
b.iter().map(capitalize_first); // fails to compile!
}
Compiler error:
error[E0631]: type mismatch in function arguments
--> src/main.rs:17:18
|
1 | fn capitalize_first(word: &str) -> String {
| ----------------------------------------- found signature of `for<'r> fn(&'r str) -> _`
...
16 | b.iter().map(capitalize_first); // fails to compile!
| ^^^^^^^^^^^^^^^^ expected signature of `fn(&&str) -> _`
A couple of questions:
map(|x| capitalize_first(x))
while map(capitalize_first)
doesn't? What sort of magic happens behind the scenes in the first case? In my view, those should be identical.&&str
and &str
?Is there a reason on why rust can't transparently convert between
&&str
and&str
?
Rust does transparently convert from &&str
to &str
, you can see it in one of your working examples:
b.iter().map(|x| capitalize_first(x))
// ^ x is a &&str here ^ but seems to be a &str here
What sort of magic happens behind the scenes[?]
The "magic" that happens is due to type coercions, specifically Deref
coercion. "Type coercions are implicit operations that change the type of a value".
Why does this work
map(|x| capitalize_first(x))
whilemap(capitalize_first)
doesn't?
What you've discovered is that while there is a coercion for &&str
to &str
, there is no coercion from fn(&&str)
to fn(&str)
. What coercions are available is pretty restricted.
On top of that, there are only specific places where coercion can occur, but what makes the first case work is that, with the closure, a coercion site is available right where you call capitalize_first
:
b.iter().map(|x| capitalize_first(x))
// ^ the compiler injects code here to do the coercion
// as if you had written capitalize_first(*x)
When you use the function directly, there is no such site where coercion can occur.
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