Callback to mutable self

Hello guys, i am new to Rust. And i am trying to make something like "Callback to mutable self"

Is there a way to send a mutable borrowed self to a callback

use std::cell::RefCell;
use std::rc::Rc;

#[derive(Clone)]
pub struct Callbacks {
    callbacks: Vec<Rc<RefCell<dyn FnMut(&mut App)>>>,
}

impl Callbacks {
    pub fn new() -> Self {
        Callbacks {
            callbacks: Vec::new(),
        } /*@*/
    }

    pub fn register<F: FnMut(&mut App) + 'static>(&mut self, callback: F) {
        let cell = Rc::new(RefCell::new(callback));
        self.callbacks.push(cell); /*@*/
    }

    pub fn call(&mut self, val: &mut App) {
        for callback in self.callbacks.iter() {
            let mut closure = callback.borrow_mut();

            (&mut *closure)(val);
        }
    }
}

struct App {
    age: u32,
    events: std::rc::Rc<std::cell::RefCell<Callbacks>>,
}

impl App {
    fn new() -> App {
        App {
            age: 23,
            events: Rc::new(RefCell::new(Callbacks::new())),
        }
    }

    fn insert<F: FnMut(&mut App) + 'static>(&mut self, event: F) {
        self.events.borrow_mut().register(event);
    }

    fn call_events(&mut self) {
        self.events.borrow_mut().call(self);
    }
}

fn main() {
    let mut app = App::new();

    app.age = 100;

    println!("Age is {}", app.age);

    app.insert(|val| {
        val.age = 123;

        println!("Hello {}", val.age);
    });

    app.call_events();
}

And i got this

   Compiling playground v0.0.1 (/playground)
error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
  --> src/main.rs:48:9
   |
48 |         self.events.borrow_mut().call(self);
   |         ------------------------^^^^^^^^^^^- ... and the immutable borrow might be used here, when that temporary is dropped and runs the destructor for type `std::cell::RefMut<'_, Callbacks>`
   |         |
   |         mutable borrow occurs here
   |         immutable borrow occurs here
   |         a temporary with access to the immutable borrow is created here ...

error: aborting due to previous error

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

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

It would be great if someone could help me with my code.

The reason it doesn't work is that the callbacks might modify the events value while it's borrowed, which wouldn't make sense. Would it be possible to factor the shared data out, so you have

struct Shared {
   age: u32
}
struct App {
    shared: Shared,
    events: Rc<RefCell<Callbacks>>,
}

And then pass a mutable reference to shared instead of to App?

5 Likes

Thank you very much !