Can't create box of FnMut

use std::ops::Drop;

struct ScopeExit {
    call_ : Box<dyn FnMut()>,
}

impl ScopeExit {
    pub fn new(call : &dyn FnMut()) ->Self {
        ScopeExit { call_: Box::<&dyn FnMut()>::new(call) }
    }
}

impl Drop for ScopeExit {
    fn drop(&mut self) {
        (self. call_)()
    }
}

Got error:

error[E0277]: expected a `Fn<()>` closure, found `dyn FnMut()`
 --> src/lib.rs:9:28
  |
9 |         ScopeExit { call_: Box::<&dyn FnMut()>::new(call) }
  |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected an `Fn<()>` closure, found `dyn FnMut()`
  |

Type of ScopExit::call_ is Box<dyn FnMut()>, why compiler tell me this expect Fn<()>?

You are trying to cast &dyn FnMut() to dyn FnMut(), which is not possible as &dyn FnMut() does not implement FnMut(). Your error message is slightly confusing, because of this blanket implementation of FnMut() for &T where T: Fn(). I honestly would consider changing your API in such a way that you let the user create the Box<dyn FnMut()> and pass it to ScopeExit::new, rather than &dyn FnMut(). I don't think there's a way to create a Box<dyn FnMut()> from a &dyn FnMut().

1 Like

This wouldn't make any sense. &dyn FnMut() is limited time borrow, Box<dyn FnMut()> is unlimited owned type.

It's like taking one-year loan from bank and then giving that money to someone permanently.

In real world you would end up in jail, in Rust this lead to compile-time error.

P.S. Of course the opposite works fine, both in real world and in Rust: if you have own money (or have onwership of closure) then you can loan it to someone temporarily.

1 Like

you probably meant to do this?

pub fn new(call : impl FnMut()) ->Self {
    ScopeExit { call_: Box::<dyn FnMut()>::new(call) }
}

Thanks, but this doesn't work.

error[E0599]: the function or associated item `new` exists for struct `Box<dyn FnMut()>`, but its trait bounds were not satisfied
   --> src/lib.rs:12:48
    |
12  |         ScopeExit { call_: Box::<dyn FnMut()>::new(call) }
    |                                                ^^^ function or associated item cannot be called on `Box<dyn FnMut()>` due to unsatisfied trait bounds
    |
   ::: /home/shylock/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:163:1
    |
163 | pub trait FnMut<Args: Tuple>: FnOnce<Args> {
    | ------------------------------------------ doesn't satisfy `dyn FnMut(): Sized`
    |
    = note: the following trait bounds were not satisfied:
            `dyn FnMut(): Sized`

For more information about this error, try `rustc --explain E0599`.

dyn FnMut() is not sized.

sorry, I was being sloppy. following should compile:

pub fn new(call : impl FnMut() + 'static) ->Self {
    ScopeExit { call_: Box::new(call) }
}

EDIT:

a little explanation: you actually create a box of the generic type (of the FnMut() bound), then the box is unsize coerced into Box<dyn FnMut()>, so the code with all types annotated should be:

    pub fn new<F>(call: F) -> Self
    where
        F: FnMut() + 'static,
    {
        ScopeExit {
            call_: Box::<F>::new(call) as Box<dyn FnMut()>,
        }
    }
1 Like

Thanks, it works.