How to convert Box<dyn Fn()> to Box<dyn FnMut()>

Please read this code:

let func:Box<dyn Fn()>=Box::new(||{});
// func as Box<dyn FnMut()>; //ERROR!
Box::new(func) as Box<dyn FnMut()>; //OK

Why Rust requires me using Box to wrap func? Can I directly convert func into Box<dyn FnMut()>?

You can't upcast to a dyn Supertrait (yet).

You're getting a double indirection there via unsized coercion. Your outer Box would call your inner Box to call the closure.

Is there a particular reason you need the specific type? It's pretty common to just rely on generics:

fn call<F: FnMut()>(mut f: F) {
    f();
}

fn main() {
    let func: Box<dyn Fn()> = Box::new(|| {});
    // This is fine without converting the type because
    // `Box<dyn Fn()>` implements `FnMut()`
    call(func);
}

Or, depending on the use case, you could maybe do something like this:

fn main() {
    let closure = || {};
    let func: Box<dyn Fn()> = Box::new(closure);
    let funk: Box<dyn FnMut()> = Box::new(closure);
}

(Might not be possible if you're capturing state.)

3 Likes

You created a trait object by boxing the original closure. I don't believe that trait objects can be upcasted in such a way.

You're right, but I using convert ||{} into Box<dyn Fn()> in this example is just for convenient to explain the problem, in actual scenario func was stored in a Vec that I don't know what type it actually is. I'll choose another way to implement it. Thank you :slightly_smiling_face:.

Note that if you really need to, you can write your own upcasting:

trait MyFn : Fn() {
    fn into_dyn_fn_mut<'slf> (self: Box<Self>)
      -> Box<dyn 'slf + FnMut()>
    where
        Self : 'slf,
    ;
}

impl<F : Fn()> MyFn for F {
    fn into_dyn_fn_mut<'slf> (self: Box<Self>)
      -> Box<dyn 'slf + FnMut()>
    where
        Self : 'slf,
    {
        self
    }
}

fn main ()
{
    let func: Box<dyn MyFn> = Box::new(|| {});
    func();
    let _: Box<dyn FnMut()> = func.into_dyn_fn_mut();
}
7 Likes

Thanks a lot, it works!

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.