Skip to content

implement get_mut_or_default #562

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions src/storage/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ pub trait GenericWriteStorage {
/// Get mutable access to an `Entity`s component
fn get_mut(&mut self, entity: Entity) -> Option<&mut Self::Component>;

/// Get mutable access to an `Entity`s component. If the component does not exist, it
/// is automatically created using `Default::default()`.
///
/// # Panics
/// Panics if the entity is dead.
fn get_mut_or_default(&mut self, entity: Entity) -> &mut Self::Component where Self::Component: Default;

/// Insert a component for an `Entity`
fn insert(&mut self, entity: Entity, comp: Self::Component) -> InsertResult<Self::Component>;

Expand All @@ -103,6 +110,14 @@ where
WriteStorage::get_mut(self, entity)
}

fn get_mut_or_default(&mut self, entity: Entity) -> &mut Self::Component where Self::Component: Default {
if !self.contains(entity) {
self.insert(entity, Default::default()).expect("Insertion of default component failed");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will panic for dead entities.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh boy. I didn't think of that.

}
// At this point it is reasonably safe to assume the component exists.
self.get_mut(entity).expect("Fetching a default component failed")
}

fn insert(&mut self, entity: Entity, comp: Self::Component) -> InsertResult<Self::Component> {
WriteStorage::insert(self, entity, comp)
}
Expand All @@ -126,6 +141,14 @@ where
WriteStorage::get_mut(*self, entity)
}

fn get_mut_or_default(&mut self, entity: Entity) -> &mut Self::Component where Self::Component: Default {
if !self.contains(entity) {
self.insert(entity, Default::default()).expect("Insertion of default component failed");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

}
// At this point it is reasonably safe to assume the component exists.
self.get_mut(entity).expect("Fetching a default component failed")
}

fn insert(&mut self, entity: Entity, comp: Self::Component) -> InsertResult<Self::Component> {
WriteStorage::insert(*self, entity, comp)
}
Expand Down
42 changes: 41 additions & 1 deletion src/storage/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ mod test {
type Storage = NullStorage<Self>;
}

#[derive(PartialEq, Eq, Debug)]
#[derive(PartialEq, Eq, Debug, Default)]
struct Cvec(u32);
impl From<u32> for Cvec {
fn from(v: u32) -> Cvec {
Expand Down Expand Up @@ -295,6 +295,42 @@ mod test {
}
}

fn test_get_mut_or_default<T: Component + Default + From<u32> + AsMut<u32> + Debug + Eq>()
where
T::Storage: Default,
{
let mut w = World::new();
let mut s: Storage<T, _> = create(&mut w);

// Insert the first 500 components manually, leaving indices 500..1000 unoccupied.
for i in 0..500 {
if let Err(err) = s.insert(Entity::new(i, Generation::new(1)), (i).into()) {
panic!("Failed to insert component into entity! {:?}", err);
}
}

for i in 0..1_000 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, what are these loops and magic numbers? am finding I have to think quite a bit to understand the logic, and then think again to figure out what its intent is.

if the loops are necessary, it's worth writing the rationale in a comment

  • is it to get the entity index high enough?
  • can we use a smaller backing storage to achieve the same effect?

not obvious to understand what the test is doing 🤔:

  1. insert 500 entities with n + 1290 as its value.
  2. mutate 1000 entities with get_mut_or_default() to be n + n + 710
  3. assert the first 500 are 2n + 2000
  4. assert the last 500 are n + 710

ah 💡! while it is logically correct and does test the effect, it's considerable mental effort to turn the code back into understanding. could you try to make it easier, otherwise my head is like this: 🤕, haha


edit: just expanded the diff, and saw the existing code is like that. oh man 😂

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the magic numbers & commented it.

*s.get_mut_or_default(Entity::new(i, Generation::new(1)))
.as_mut() += i;
}

// The first 500 were initialized, and should be i*2.
for i in 0..500 {
assert_eq!(
s.get(Entity::new(i, Generation::new(1))).unwrap(),
&(i + i).into()
);
}

// The rest were Default-initialized, and should equal i.
for i in 500..1_000 {
assert_eq!(
s.get(Entity::new(i, Generation::new(1))).unwrap(),
&(i).into()
);
}
}

fn test_add_gen<T: Component + From<u32> + Debug + Eq>()
where
T::Storage: Default,
Expand Down Expand Up @@ -391,6 +427,10 @@ mod test {
test_get_mut::<Cvec>();
}
#[test]
fn vec_test_get_mut_or_default() {
test_get_mut_or_default::<Cvec>();
}
#[test]
fn vec_test_add_gen() {
test_add_gen::<Cvec>();
}
Expand Down