简体   繁体   中英

If or How to use references and lifetimes

I am experimenting rust by implementing a simple game, and quickly ran into a situation where I find it is hard to decide what is a better approach and I think it is related to a broader question about references and lifetimes in Rust, hopefully, someone has better experience on this.

Assuming I have a Game that represents the game world, and GameObject is the trait for all item in the game. And a GameState to keep track of object collisions for every update, Collision will need to hold reference to other GameObject in order to tell which ones collide, in order to do that I will need to add a lifetime to the Collision then add lifetimes to GameState and then add to Game if I want the Game object to hold the state, Or I can maybe copy the GameState for every update tick.

trait GameObject {
   fn draw(&self, renderer: &Renderer);
   fn update(&mut self, state: &GameState, delta: f64);
}

struct Collision<'a> {
   obj1: &'a Box<dyn GameObject>,
   obj2: &'a Box<dyn GameObject>
}

struct GameState<'a> {
   // other states...
   collisions: Vec<Collision<'a>>,
}

struct Game {
   items: Box<dyn GameObject>,
}

impl Game {
   fn draw(&self, renderer: &Renderer){
      for item in &self.items {
         item.draw(render);
      }
   }
   fn update(&mut self, delta: f64){
      // create a state everytime? or hold a reference to it?
      for item in &self.items {
         item.update(delta);
      }
   }
}

So that's where I am 100% sure what would be the best approach, or in general, once we give a lifetime to an object, then any other object holds this object will need lifetime types, not maybe we should reorganize the code structure to avoid lifetimes? Thanks.

My advice would be to use lifetimes inside temporary structs but avoid them for structs you want to keep around. So if you create a GameState for every frame, add to it, use it and destroy it, that's ok. But if you want to keep it alive for many frames, then those references will be a problem, because you will be keeping your objects borrowed all the time.

If you have complex references between your objects, there are several solutions that do not require lifetimes and persistent borrows:

  • Use Rc<dyn GameObject> instead of Box and store Weak versions of the pointer everywhere except in the main container.
  • Store the objects in a SlotMap and use a key to refer to the objects.
  • Store the objects in a Vec and use the index to refer to the objects. It is just like the SlotMap but simpler. Still useful for simpler cases such as if your objects are just created once and then never added or removed.
  • Use an Entity-Component-System (ECS) library, such as Bevy suggested by @tadman in the comment above.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM