Avoiding an explicit call to DerefMut::deref_mut when dealing with &mut reference

Hello,
the code below implements a simple Callback struct that contains a boxed FnMut. I implemented DerefMut to be able to call it.
The call through DerefMut however only works when not using a &mut reference (using the &mut * trick I found here).

The error message when using a &mut reference is baffling, because it talks about & references, which I can only explain when Deref is called instead of DerefMut.

Is there a way to make this work when I already have a &mut reference, or must I explicitly call deref_mut()?

use std::ops::{Deref, DerefMut};

struct Callback(Box<dyn FnMut()>);

impl Callback {
    fn new(callback: impl FnMut() + 'static) -> Self {
        Self(Box::new(callback))
    }
}

impl Deref for Callback {
    type Target = Box<dyn FnMut()>;
    
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl DerefMut for Callback {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

fn main() {
    let mut cnt = 0;
    let mut callback = Callback::new(move || {
        cnt += 1;
        println!("Test {}", cnt);
    });
    
    // This works.
    (&mut *callback)();
    
    let callback_ref: &mut Callback = &mut callback;
    
    // This works, but I want to avoid the explicit call to deref_mut.
    callback_ref.deref_mut()();
    
    // This one does not.
    // callback_ref();
    
    // This one also does not work, but I expected it to.
    (&mut *callback_ref)();
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0596]: cannot borrow data in a `&` reference as mutable
  --> src/main.rs:44:5
   |
44 |     (&mut *callback_ref)();
   |     ^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable

error: aborting due to previous error

For more information about this error, try `rustc --explain E0596`.
error: Could not compile `playground`.

To learn more, run the command again with --verbose.

Hi, I'm not sure why they don't work but (&mut **callback_ref)() works.

1 Like

Deref / DerefMut are nice sugar when Rust can figure out it needs to use it, which it clearly cannot when dealing with the Fn / FnMut / FnOnce calling sugar (i.e., the () operator).

The best thing you can do, is use a new borrowing binding like callback_ref, except it uses deref_mut() logic instead of just a &mut _ borrow. I have named this operatior as_FnMut for clarity, and, as a last improvement, I have replaced the double indirection of &mut Box<dyn ...> by &mut dyn ...:

impl Callback {
    #[inline]
    fn as_FnMut (self: &'_ mut Self) -> &'_ mut (dyn FnMut() + 'static)
    {
        &mut *self.0
    }
}

fn test (callback: &'_ mut Callback)
{
    let callback = callback.as_FnMut();
    
    callback();
    callback();
    callback();
    (&mut *callback)();
}

fn main ()
{
    let mut callback = {
        let mut cnt = 0;
        Callback::new(move || {
            cnt += 1;
            println!("Test {}", cnt);
        })
    };
    test(&mut callback);
}
1 Like

That is a really nice solution and performance improvement!

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.