| The Entity Component System (ECS) pattern seems to side-step the Rust borrow checker entirely in order to solve the following issues, all at the same time: 1.) allow a "parent" entity to have a reference to a "related" entity, 2.) allow such a reference to a "related" entity to be mutable, 3.) allow multiple "parent" entities to reference the same "related" entity, and 4.) allow the overall system to deallocate a "related" entity at any time without invalidating the state of all the observing "parent" entities. I have never written a game before, so Catherine West[1] might have very good reasons for choosing ECS, but I... am not so crazy about it. ECS seems to replace Rust references (raw pointers) and/or Rust smart pointers with indexes into one, large "registry" (a container: e.g., a `Vec<T>`) of entities (e.g., `struct` instances). In other words, instead of allowing a Rust pointer (a managed memory address) to keep a piece of memory alive, ECS chooses to have a "registry" keep a piece of memory alive under a particular index, managing allocation and deallocation manually. In a sense, ECS dumps Rust memory management in favor of... writing the good, ol', data-and-function -oriented C. Quite needlessly (?), since the same (?) can probably be accomplished with Rust reference counted pointers[2], weak pointers[3] and interior mutability[4]. ----- CUT HERE ----- use std::rc::{Rc, Weak};
use std::cell::RefCell;
type WeakMutEntity = Weak<RefCell<Entity>>;
struct Entity {
related: WeakMutEntity,
}
impl Entity {
fn new(related: WeakMutEntity) -> Self {
Self { related }
}
fn use_related(&mut self) {
let Some(related) = self.related.upgrade() else { return; };
related.borrow_mut().use_related();
}
}
let entity1 = Rc::new(RefCell::new(
Entity::new(/* null */ Weak::new())));
let entity2 = Rc::new(RefCell::new(
Entity::new(Rc::downgrade(&entity1))));
entity2.borrow_mut().use_related();
----- CUT HERE -----If the above does not get the job done, the solution shouldn't be to just abandon the Rust borrow checker; the solution should be to get the Rust Gods to optimize the available pointer types syntax-wise and/or performance-wise. [1] https://www.youtube.com/watch?v=aKLntZcp27M [2] https://doc.rust-lang.org/book/ch15-04-rc.html [3] https://doc.rust-lang.org/book/ch15-06-reference-cycles.html [4] https://doc.rust-lang.org/book/ch15-05-interior-mutability.h... |
No construction made up of any kind of pointers can achieve that, unless there's a Sufficiently Smart compiler that can magically fully eliminate these pointers.