How to organize 1 read/write and 1 read only reference on the heap

I have a project with the following objects:

  • Machine - contains state that can be updated by simulator and output that is a part of the state
  • BaseSimulator - contains logic to update a model and notify listener on new state and output
  • MachineListener - listen to simulator updates and represent state or output changes on the screen
let machine = Box::new(Machine::new());
let listener = Box::new(MachineListener { model: machine });
let mut simulator = BaseSimulator::new(machine, listener);

The reason that both Machine and MachineListener enter as heap values is because the BaseSimulator has to simulate different models and notify different listeners that follow a specific iterface / trait.

How do pass Machine to the BaseSimulator mutably so it can change it, and at the same time, how to pass Machine to the listener so it can use some of it read-only functions. I tried to use Rc but go blocked by the compiler since baseSimulator needs a mutable Machine. Is there something along the lines of Rc::get_mut but im not sure i understand it? I think its could be done with lifetimes too but i dont want to go that path if its not the preferred one if possible.

I think you need to share more of your code to make your issue clear. Based on the code you shared, BaseSimulator owns the other two objects and this can do whatever it wants with them. Thus the problem you're having must be elsewhere in your code.

1 Like

I stripped my full project to absolute minimum and here is an example playground.

There i'm trying to initialize the listener with a machine, alternatively the EventListener trait can pass the read only reference to the model on notify_state_change() function, but here im also unsure how to organize a Box for Simulator and read only for listener. In any case i want to make sure Simulator is not responsible for creating a listener and shouldn't know how its implemented.

Ah, that is much more clear. If you want mutable access to an object that is referenced in multiple structures, you probably need interior mutability:
Note that I wrote this with Arc and Mutex, but if you don't use threads you could use Rc and RefCell in the same way.

The other option, probably better, would be to pass the Model into the listener method as a parameter, so you could have something like:

trait EventListener T {
  fn forgot_name<M: Model>(&self, time: i32, model: &M);

Then you don't store your model in the listener, which simplifies a lot.

1 Like

I would like 2nd option, its literally what they do in C++ book im trying to copy. But it looks like BaseSimulator wont allow it, since it expect a trait object EventListener and traits cant have generics according to become trait objects according to the book.

You can still have &dyn types as inputs to methods, so you could try something like this: playground.

1 Like

Wow, i was trying to do exactly that for the last 3 hours :rofl: awesome, thanks, gonna study your code and take it from there!

Do you by any change know how to use traits to express one to many relationship? So in this code the MachineListener takes any AtomicModel, but id like to express the fact that a concrete model Machine that implements AtomicModel can have any number of listeners that implement fn notify() for this concrete Machine?

When you define the listener trait, you would give the trait itself a parameter, something like

trait<M: AtomicModel> Listener<M> {
   fn listen(&self, model: &M);

I think that would work for your code, but haven't tried it.