The compiler does not convert the type automatically

Problem

as you can see, this code doesn't work`

fn main(){
    a([Box::new(||println!("AAAA"))].as_slice())
}


fn a(func:&[Box<dyn FnOnce()->()>]){

}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
 --> src/main.rs:2:7
  |
2 |     a([Box::new(||println!("AAAA"))].as_slice())
  |     - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&[Box<dyn FnOnce()>]`, found `&[Box<[closure@main.rs:2:17]>]`
  |     |
  |     arguments to this function are incorrect
  |
  = note: expected reference `&[Box<(dyn FnOnce() + 'static)>]`
             found reference `&[Box<[closure@src/main.rs:2:17: 2:19]>]`
note: function defined here
 --> src/main.rs:6:4
  |
6 | fn a(func:&[Box<dyn FnOnce()->()>]){
  |    ^ -----------------------------

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` due to previous error

But if you change it, it will work`

fn main(){
    a([Box::new(||println!("AAAA")) as Box<dyn FnOnce()->()>].as_slice()  )
}


fn a(func:&[Box<dyn FnOnce()->()>]){

}

how to change this code so you don't have to write it every time

The general rule is that you have to trigger the coercion from Box<{closure...}> to Box<dyn Fn...> before the Box is put in any other container (here an array). There are several ways to do this. One is to insert an as where you have it, but you don't have to specify the type:

fn main() {
    a([Box::new(|| println!("AAAA")) as _].as_slice())
}

This means the type of the array element is not known from the element expression Box::new(|| ...) as _, since it is the result of a cast to an unspecified type, and the compiler will instead infer the type of the array (and of the cast result) from the call to a.

Another way, which might be useful when the type is still ambiguous or you are doing lots of this and want it to be more robust (better type errors when a mistake is made), is to write a helper function:

fn main() {
    a([yeller(|| println!("AAAA"))].as_slice())
}

fn yeller(func: impl FnOnce() + 'static) -> Box<dyn FnOnce()> {
    // The coercion to trait object happens when the box is returned
    Box::new(func)
}

In general, it is often useful to structure your code so that a closure || ... is passed directly to a function that demands a specific type for the closure; not doing this can result in the wrong type being inferred, particularly when the closure accepts references as parameters, so there are uses for this pattern beyond dyn.

4 Likes

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.