Search code examples
rustdesign-patternsclosureslifetime

How to hold a (self-captured) closure for an observer design model


I am trying to hold multiple callbacks as closures, but I'm having troubles with their lifetime:

pub type WriteCallback = Box<dyn FnMut(u8, u8) -> u8>;

pub struct AddressBus {
    on_write_listeners: HashMap<usize, WriteCallback>,
}

impl AddressBus {
    pub fn on_write(&mut self, address: usize, callback: WriteCallback) {
        self.on_write_listeners.insert(address, callback);
    }
}

// component.rs

pub struct Component {
    value: u8,
    address_bus: Rc<RefCell<AddressBus>>,
}

impl Component {
    pub fn new(address_bus: Rc<RefCell<AddressBus>>) -> Self {
        Component {
            value: 0,
            address_bus: address_bus.clone(),
        }
    }

    pub fn init(&mut self) {
        self.address_bus.clone().borrow_mut().on_write(0xFF54, Box::new(|old, new| {
            // I want to use self here e.g: self.value = new;
            new
        }));

        self.address_bus.clone().borrow_mut().on_write(0xFF55, Box::new(|old, new| {
            // I want to use self here
            new
        }));
    }
 }

But no matter what i try, i always get:

32 |       pub fn init(&mut self) {
   |                   - let's call the lifetime of this reference `'1`
33 |           self.address_bus.clone().borrow_mut().on_write(0xFF54, Box::new(|old, new| {
   |  ________________________________________________________________^
34 | |             // I want to use self here
35 | |             self.value = new;
36 | |             new
37 | |         }));
   | |__________^ cast requires that `'1` must outlive `'static`

Is there a way to fix this, or do I need to change to a different design pattern? Knowing that I am using wasm-bindgen and can't use lifetime references inside my structures.

I tried to use move like self.address_bus.clone().borrow_mut().on_write(0xFF54, Box::new(move .... It works but it also consumes self and I am no longer able to use the struct.


Solution

  • I managed to make it work by following cdhowie's advice, using Rc<RefCell<Self>>:

    fn init(rc: Rc<RefCell<Self>>) {
        let rc2 = rc.clone();
    
        rc.clone().borrow_mut().address_bus.borrow_mut().on_write(0xFF54, Box::new(move |old, new| {
            new
        }));
        rc2.clone().borrow_mut().address_bus.borrow_mut().on_write(0xFF55, Box::new(move |old, new| {
            rc2.borrow_mut().value = old;
            new
        }));
    }
    

    In order to modify self in both, I need to clone the reference and move it.