Question about type inference with trait objects in match arms

I was wondering if there was a way to force a variable to be a trait object. I had a situation where I wanted to get a closure from a match, so I boxed the closures up. However, the compiler still got angry about the match arms having incompatible types.
I have the following condensed example (playground):

#![allow(dead_code)]
struct Noise {
    callback: Option<Box<dyn Fn(&Self, f64) -> f64 + Sync>>
}

impl Noise {
    pub fn noise(&self, _p: f64) -> f64 {
        unimplemented!()
    }

    pub fn wrap(mut self, freq: f64) -> Self {
        //let new_callback: Box<dyn Fn(&Self, f64) -> f64 + Sync> = match self.callback {
        let new_callback = match self.callback {
            Some(callback) => Box::new(
                move |noise: &Self, p: f64| callback(noise, p * freq)
            ),
            None => Box::new(
                move |noise: &Self, p: f64| noise.noise(p * freq)
            )
        };
        
        self.callback = Some(new_callback);
        self
    }
}

which gives

Compiler output
   Compiling playground v0.0.1 (/playground)
error[E0308]: `match` arms have incompatible types
  --> src/lib.rs:17:21
   |
13 |             let new_callback = match self.callback {
   |    ____________________________-
14 |   |             Some(callback) => Box::new(
   |  _|_______________________________-
15 | | |                 move |noise: &Self, p: f64| callback(noise, p * freq)
16 | | |             ),
   | |_|_____________- this is found to be of type `std::boxed::Box<[closure@src/lib.rs:15:17: 15:70 callback:_, freq:_]>`
17 |   |             None => Box::new(
   |  _|_____________________^
18 | | |                 move |noise: &Self, p: f64| noise.noise(p * freq)
19 | | |             ),
   | |_|_____________^ expected closure, found a different closure
20 |   |         };
   |   |_________- `match` arms have incompatible types
   |
   = note: expected type `std::boxed::Box<[closure@src/lib.rs:15:17: 15:70 callback:_, freq:_]>`
            found struct `std::boxed::Box<[closure@src/lib.rs:18:17: 18:66 freq:_]>`
   = note: no two closures, even if identical, have the same type
   = help: consider boxing your closure and/or using it as a trait object

Of course, I can solve this by giving new_callback an explicit type, as seen in the commented out line. But I was wondering if there was a better/shorter way to convince the compiler that these arms have compatible types.

Just add as _ to both match arms:

let new_callback = match self.callback {
    Some(callback) => Box::new(
        move |noise: &Self, p: f64| callback(noise, p * freq)
-    ),
+    ) as _,
    None => Box::new(
        move |noise: &Self, p: f64| noise.noise(p * freq)
-    ),
+    ) as _,
};

This will give the compiler the freedom necessary to make a coercion.

1 Like

D'oh! It was so simple.