Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I get a "type mismatch" error when passing a function directly but it works with an equivalent closure?

Tags:

rust

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:

  1. Why does this work 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.
  2. Is there a reason on why rust can't transparently convert between &&str and &str?
like image 421
Ahmed Farghal Avatar asked Sep 05 '21 19:09

Ahmed Farghal


1 Answers

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)) while map(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.

like image 147
kmdreko Avatar answered Oct 25 '22 19:10

kmdreko