Cannot pass a &mut self to a reference stored as a member


#1

Please can somebody advise me on the following issue.
This is the simplified version of a program I’m trying to build:

trait CallbackHandler {
    fn callback(&mut self);
}

struct CallbackRegistry<'a> {
    handlers: Vec<&'a mut CallbackHandler>
}

impl<'a> CallbackRegistry<'a> {
    pub fn new() -> CallbackRegistry<'a> {
        CallbackRegistry {
            handlers: Vec::with_capacity(10)
        }
    }

    pub fn register(&mut self, handler: &'a mut CallbackHandler) {
        self.handlers.push(handler);
    }
}

struct Handler1<'a> {
    registry: &'a mut CallbackRegistry<'a>
}

impl<'a> Handler1<'a> {
    pub fn new(registry: &'a mut CallbackRegistry<'a>) -> Handler1<'a> {
        Handler1 {
            registry: registry
        }
    }

    pub fn start(&'a mut self) {
        // does not compile with "cannot borrow `*self` as mutable more than once at a time"
        // self.registry.register(self);
    }
}

impl<'a> CallbackHandler for Handler1<'a> {
    fn callback(&mut self) {
    }
}

The problem is in the line:

self.registry.register(self);

which understandably does not complie. The question is - what’s the best way to go around this problem? One thing I can think of is that instead of storing a reference to registry as a member of Handler1 I should rather pass it as an argument to function “start”. To me it’s not very desirable as that would mean I have to pass a reference to registry all over the places in my application bloating the code and making unit testing more difficult. It would be also not nice from design perspective as the callers of start method do not necessarily need to know anything about “registry” .

I can imagine there will be lots of occasions where storing a borrowed pointer as a struct member would cause compilation problems which makes me think that common OOP techniques do not work very well in Rust. Is there anything I’m missing?


#2

I just implemented similar callback traits today for a small project. The solution I used for my particular problem was to pass self in at self.registry.register(self); as a raw mutable pointer (*mut rather than &mut).


#3

But using raw pointers would mean unsafe blocks when derefencing them. It can work, but I’m reluctant to resort to unsafe for such basic things. It kind of deteriorates the purpose of Rust with its safety. I would rather program in C++.


#4

In what way is making such circular dependencies OOP? There doesn’t seem to be a need for Handler to know about Registry. I’m surprised that such coupling makes testing easier.


#5

Ok, maybe I should have expanded the example a bit more.
Handler1 could be an instance of some general interface with start() method. The “registry” is just something that is needed for Handler1 to do its work (on the back of the callbacks). There can be another handler (Hander2) of the same interface but without need to register in the registry. So I don’t really want to pass “registry” to “start” if it’s something specific to Handler1 only. Also if I decide to mock a Handler interface in a unit test I would have to somehow create instance of registry which is not used in the mock object at all.


#6

If a handler can have a legitimate need to access the registry this seems like a job for RefCell. If you put the handlers vector into a cell, you can avoid &mut references to the registry.


#7

Ok, thanks. RefCell might be the answer.