How to deal with cyclic mutable borrows

Viewed 132

I am working on building a small game in Rust and have run into a problem I'm not sure how to solve. Essentially I would like to have a Controller trait which has an update method. The update method should accept a mutable entity reference and have a mutable reference to itself. The problem is that I want the entity to own its controller, which results in me needing to do a double mutable borrow. Here is a Rust Playground link which demonstrates what I am trying to do https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f33f548bb72373f47757092fbb0b615d


trait Controller{
    fn update(&mut self, entity: &mut Entity);
}

struct MyController{

}

impl Controller for MyController{
    fn update(&mut self, _entity: &mut Entity){

    }
}

struct Entity{
    controller: Option<Box<dyn Controller>>    
}

impl Entity{

    fn update(&mut self){
        if let Some(controller) = self.controller.as_mut(){
            controller.update(self);
        }
    }
}

fn main() {
    let _entity = Entity{
        controller: Some(Box::new(MyController{}))
    };
}
2 Answers

The best way that I've found to tackle this problem is by splitting borrows. Instead of passing the whole Entity you would take everything that is required by the Controller.update method and put it in another struct (e.g. EntityData). Then instead of passing the entire Entity you would just pass the EntityData.

The Entity.update method would then look something like this:

impl Entity{
    fn update(&mut self){
        // Borrow the controller
        let controller = &mut self.controller;
        // Borrow the entity data
        let data = &mut self.data;

        if let Some(controller) = controller.as_mut() {
            controller.update(data);
        }
    }
}

Playground Link

Splitting borrows works in most cases because you don't really need to borrow all the fields of the struct. In your case, you shouldn't need to pass the controller a reference to itself, so borrowing everything except that should be adequate.

If you intend to compile your code with existing design approach, you could do something like this:

impl Entity{

    fn update(&mut self){
      // Take controller out of its original place 
      if let Some(mut contrl) = self.controller.take() {
           contrl.update(self);
           // replace it back
           self.controller = Some(contrl);
       }
    }
}

Playground

In my opinion, you should try to avoid these kinds of circular dependencies.

Related