I'm trying to get the following test to pass but am struggling to (a) annotate the necessary lifetimes and (b) understand where I am trying to "move out of borrowed context":
#[test]
fn entity_builder_associates_component_with_entity() {
let mut entity_manager = EntityManager::new();
let entity_id = entity_manager
.new_entity()
.add_component(Transform { x: 0.0, y: 0.0 })
.create();
entity_manager.update();
if let Some(found_id) = entity_manager.entities_with("transform").get(0) {
assert_eq!(found_id, &entity_id);
} else {
panic!(false);
}
}
Which currently gives me the following error:
error[E0507]: cannot move out of borrowed content
--> src/core/entity.rs:198:25
|
198 | let entity_id = entity_manager
| _________________________^
199 | | .new_entity()
200 | | .add_component(transform)
| |_____________________________________^ cannot move out of borrowed content
I'll post what I think is the relevant implementation code below. I'm struggling to understand why this error occurs because as far as I can tell the transform
is owned and is being consumed by the add_component
method.
I get that once pushed onto the vec it is then borrowed, which is where I'm having difficulty specifying lifetimes. I think I need a 'b
on the components vec
and possibly in the signature of add_component
. But I think I might be having issues due to wanting EntityManager
eventually own the Components
(which is just Transform
for now), which the EntityBuilder
initially owns, therefore I think the components need a lifetime of at least as long as EntityManager
, but because EntityBuilder
has a reference to the manager, that also needs a similarly long lifetime, and as you can see, I've confused myself considerably!
Any help greatly appreciated.
Now for the implementation code as it stands:
type EntityId = usize;
enum Operation {
AddEntity(EntityId, Vec<Transform>),
RemoveEntity(EntityId, usize),
}
struct EntityBuilder<'a> {
entity_manager: &'a mut EntityManager,
entity_id: EntityId,
components: Vec<Transform>,
}
impl<'a> EntityBuilder<'a> {
fn new(entity_manager: &'a mut EntityManager) -> EntityBuilder {
let entity_id = entity_manager.next_id();
EntityBuilder {
entity_manager,
entity_id,
components: vec![],
}
}
pub fn add_component(&mut self, component: Transform) -> &'a mut EntityBuilder {
self.components.push(component);
self
}
pub fn create(self) -> EntityId {
self.entity_manager.add_entity(self.entity_id, self.components);
self.entity_id
}
}
struct EntityManager {
next_id: EntityId,
next_entity_index: usize,
entities: Vec<EntityId>,
entity_indexes: HashMap<EntityId, usize>,
components: HashMap<String, Transform>,
operations: Vec<Operation>,
}
impl EntityManager {
pub fn new() -> EntityManager {
EntityManager { ... }
}
pub fn new_entity(&mut self) -> EntityBuilder {
EntityBuilder::new(self)
}
pub fn entities_with(&self, component_type: &str) -> Vec<EntityId> {
vec![-1]
}
pub fn update(&mut self) {
while !self.operations.is_empty() {
match self.operations.pop().unwrap() {
AddEntity(entity_id, components) => self.save_entity(entity_id, components),
}
}
}
fn next_id(&mut self) -> EntityId { // Increment and return next_id + 1 }
fn add_entity(&mut self, entity_id: EntityId, components: Vec<Transform>) {
self.operations.push(AddEntity(entity_id, components));
}
fn save_entity(&mut self, entity_id: EntityId, mut components: Vec<Transform>) {
...
self.components.insert(String::from("transform"), components.pop().unwrap());
}
}