Ownership model help


#1

C programmer here, trying to implement a Reactor-EventHandler toy application, to learn Rust.

I am a bit lost with what ownership modelling I should be implementing for something like the code below.

Should I be using Rc/RefCell? Lifetimes? Inner/Outer objects?

struct EventHandler {
    reactor: Reactor,
}
 
impl EventHandler {
    fn handle(&mut self) {
        println!("Handled");
        self.reactor.unregister(self);
    }
    fn read(&mut self) {
        self.reactor.register(self);
    }
}
 
struct Reactor {
    event_handlers: Vec<EventHandler>,
}
 
impl Reactor {
    fn register(&mut self, e: &EventHandler) {
        self.event_handlers.push(e);
    }
    fn unregister(&mut self, e: &EventHandler) {
 
    }
    fn run(&mut self) {
        for h in &self.event_handlers {
 
        }
    }
    fn createHandler(&mut self) -> EventHandler {
        EventHandler{
            reactor: self,
        }
    }
}
 
fn main() {
    let mut r = Reactor{
        event_handlers: Vec::new(),
    };
 
    let mut e = r.createHandler();
 
    e.read();
 
    r.run();
}

#2

Do not try to use & as * in C. Rust references are not pointers (some of them compile down to pointers, but conceptually they’re a different thing. In the same way like in C pointers are not integers — even tough void* may compile to the same thing as unsigned long, using one in place of the other often doesn’t make sense).

In Rust references are more like temporary read-only or exclusive-write locks on objects. You use them for letting functions inspect or temporarily use, but not keep, some data. In structs you use references rarely, mostly to temporarily hold or manipulate something that lives on the stack or was lended by function’s caller.

For storing values in Vec<EventHandler> you will need owned EventHandler. Don’t worry about it looking like pass-by-value or copying. In most cases it will be optimized out. If you definitely want a pointer, then Box<EventHandler> is an owned (permanent) analogue to a reference (temporary) &EventHandler.

Owned values in Rust are only moved, not copied, so they can only ever exist in one place at a time. Because you have both register and unregister that take an event handler, it implies that it must exist in the code in more than one place at a time. To let a value exist as owned in more than one place you need Rc/Arc.


#3

Not sure what exactly you envision for your API, but here is a rough sketch of one approach. The gist is Reactor owns the event handlers, and runs them. Callers, upon registering a handler, receive an id (token) back, which they can use to unregister or otherwise get access to an event handler later.

I’d try to stick with normal references before looking into shared ownership with interior mutability (ie Rc<RefCell<...>>).