Skip to content
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

Adds StorageEntry to Storage for convenience #274

Merged
merged 7 commits into from
Nov 2, 2017
Merged

Conversation

Aceeri
Copy link
Member

@Aceeri Aceeri commented Sep 27, 2017

Fixes #268

Adds StorageEntry to allow for in place insertions, also some other methods for dealing with the occupied and vacant entries, similar to stds HashMap Entry.

Still need to add some tests for further verification (I'll do this tomorrow).

@@ -203,6 +317,20 @@ where
}
}

/// Returns an entry to the component associated to the entity.
///
/// Note: This does not immediately error if the entity is dead.
Copy link
Member

Choose a reason for hiding this comment

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

I think we should use Result here.

Copy link
Member Author

Choose a reason for hiding this comment

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

Are we sure? That would make this require a bit more boilerplate for this stuff then:

if let Ok(entry) = storage.entry(entity) {
    entry.or_insert(component);
}

At least until we get try to work outside of just functions.

Copy link
Member

@zakarumych zakarumych Sep 27, 2017

Choose a reason for hiding this comment

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

And now the boilerplate is at another location

if let Some(component) = storage.entry(entity).or_insert(...) {
    ...
}

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm somewhat worried about enforcing that Entry is only created with a live Entity, is there a case where an entity could be deleted after you create an entry?

Copy link
Member

Choose a reason for hiding this comment

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

It can be killed atomically. But atomically killed enities are considered alive until maintenance.

Copy link
Member Author

Choose a reason for hiding this comment

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

True, I messed around with it and couldn't find an easy way to kill the entity while having an entry, so I guess it should be fine.

pub fn or_insert_with<F>(self, component: F) -> Option<&'a mut T>
where F: FnOnce() -> T,
{
self.or_insert(component())
Copy link
Member

@zakarumych zakarumych Sep 27, 2017

Choose a reason for hiding this comment

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

The whole idea of the method is not to call closure unless component is required.
Instead or_insert should be implemented as or_insert_with(|| component).

Copy link
Member

@torkleyy torkleyy left a comment

Choose a reason for hiding this comment

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

In addition to the comments below, could you please add tests which

  • explicitly match the enum and
  • insert by using an entry and create another entry checking if it's now occupied

@@ -203,6 +317,25 @@ where
}
}

/// Returns an entry to the component associated to the entity.
///
Copy link
Member

Choose a reason for hiding this comment

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

Please add a usage example here.

}

/// Returns the entity of the current entry.
pub fn entity(&self) -> &Entity {
Copy link
Member

Choose a reason for hiding this comment

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

Is there a reason for us to give a reference to the entity?

self.or_insert_with(|| component)
}

/// Inserts a component using a default function.
Copy link
Member

Choose a reason for hiding this comment

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

"using a default function"? Why "default"?

Copy link
Member Author

Choose a reason for hiding this comment

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

Default should probaby be "lazily called function"

Vacant(VacantEntry<'a, 'b, T, D>),
}

pub enum EntryError {
Copy link
Member

Choose a reason for hiding this comment

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

This belongs into the error module. Also, we should not have enums with just a single variant.


/// Inserts a value into the storage and returns the old one.
///
/// Return value can be `None` if the entity passed in was dead.
Copy link
Member

Choose a reason for hiding this comment

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

Is this still true? Given that we now return an error, shouldn't this return just T?

Copy link
Member Author

Choose a reason for hiding this comment

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

This isn't true, thought I removedid that line.

/// Removes the component from the storage and returns it.
///
/// Return value can be `None` if the entity passed in was dead.
pub fn remove(&mut self) -> Option<T> {
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.

{
/// Inserts a value into the storage and returns the old one.
///
/// Return value can be `None` if the entity passed in was dead.
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

@Aceeri Aceeri force-pushed the entry branch 3 times, most recently from a4d3065 to 72efbc5 Compare October 6, 2017 05:25
src/error.rs Outdated

/// Storage entry error when the request entity was dead.
#[derive(Debug)]
pub struct EntryIsDead(pub Entity);
Copy link
Member

Choose a reason for hiding this comment

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

Why aren't you just using WrongGeneration? And in case this actually needs its own error, it should still be representable with specs::error::Error (see WrongGeneration; the idea is that every error can be converted to that enum).

D: Deref<Target = MaskedStorage<T>>,
{
/// Get a reference to the component associated with the entity.
pub fn get(&self) -> Option<&T> {
Copy link
Member

Choose a reason for hiding this comment

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

If the entry is occupied, how could this call return None?

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah, missed that one when removing all the others.

pub fn get_mut(self) -> &'a mut T {
match self.storage.get_mut(self.entity) {
Some(component) => component,
None => { panic!("`OccupiedEntry` on an entity without a component."); },
Copy link
Member

Choose a reason for hiding this comment

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

No need for { and }

Copy link
Member

@torkleyy torkleyy Oct 6, 2017

Choose a reason for hiding this comment

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

I know why the coverage goes down: coveralls thinks that this is an uncovered line, but that's not the case. Speaking of which, this should use unreachable!.

pub fn remove(self) -> T {
match self.storage.remove(self.entity) {
Some(old) => old,
None => { panic!("`OccupiedEntry` on an entity without a component."); },
Copy link
Member

Choose a reason for hiding this comment

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

Meh, the whole sense of Entry API is that you usually don't do any checks twice which is the case for all these methods :(

/// Inserts a value into the storage.
pub fn insert(self, component: T) -> &'a mut T {
match self.storage.insert(self.entity, component) {
InsertResult::Updated(_) => { panic!("`VacantEntry` on an entity with a component."); },
Copy link
Member

Choose a reason for hiding this comment

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

No need for { and }

None => { panic!("Storage insertion into entity {:?} failed on `VacantEntry`", self.entity); },
}
},
InsertResult::EntityIsDead(_) => { panic!("`VacantEntry` on a dead entity: {:?}.", self.entity); },
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

///
/// Behaves somewhat similarly to `std::collections::HashMap`'s entry api.
///
///## Example
Copy link
Member

Choose a reason for hiding this comment

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

missing space

Copy link
Member Author

Choose a reason for hiding this comment

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

I had them previously and it makes them not render correctly for whatever reason.

Copy link
Member

Choose a reason for hiding this comment

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

That's strange; our other examples have spaces, too.

Copy link
Member Author

Choose a reason for hiding this comment

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

Alright, seems to work now?

///## Example
///
/// ```rust
///# extern crate specs;
Copy link
Member

Choose a reason for hiding this comment

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

missing space in this and the following lines

@torkleyy
Copy link
Member

Are you still interested in this feature? I'm not sure now if I should be waiting for it, that's why I'm asking.

@Aceeri
Copy link
Member Author

Aceeri commented Oct 17, 2017

I'll finish it up, just kind of got carried away with other stuff

@Aceeri Aceeri force-pushed the entry branch 4 times, most recently from 5c093ae to 904003e Compare October 20, 2017 04:50
@torkleyy torkleyy added the ready label Oct 21, 2017

/// Inserts a value into the storage and returns the old one.
pub fn insert(&mut self, mut component: T) -> T {
std::mem::swap(&mut component, unsafe { self.storage.data.inner.get_mut(self.entity.id()) });
Copy link
Member

Choose a reason for hiding this comment

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

could use self.get_mut()

Copy link
Member Author

Choose a reason for hiding this comment

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

Annoyingly I can't figure out the lifetimes that would work for get_mut that allows just &mut self, so I can't get that to work properly.

}

/// Removes the component from the storage and returns it.
pub fn remove(self) -> T {
Copy link
Member

Choose a reason for hiding this comment

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

random impractical thought: would be fun to return (T, VacantEntry<...>)

}

impl<'a, 'b, T, D> StorageEntry<'a, 'b, T, D>
where T: Component,
Copy link
Member

Choose a reason for hiding this comment

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

is this an actual formatting of rustfmt-nightly?

Copy link
Member

Choose a reason for hiding this comment

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

No, I don't think so


/// Inserts a component using a lazily called function that is only called
/// when inserting the component.
pub fn or_insert_with<F>(self, component: F) -> &'a mut T
Copy link
Member

Choose a reason for hiding this comment

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

I think the F-typed argument should be named something other than component? stdlib uses default

/// }
/// # }
/// ```
pub fn entry<'a>(&'a mut self, e: Entity) -> Result<StorageEntry<'a, 'e, T, D>, ::error::WrongGeneration>
Copy link
Member

Choose a reason for hiding this comment

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

any reason we need to use a global type path here?

let gen = self.entities
.alloc
.generations.get(e.id() as usize)
.map(|gen| *gen)
Copy link
Member

Choose a reason for hiding this comment

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

nit: could be cloned?

@torkleyy
Copy link
Member

torkleyy commented Nov 2, 2017

Only conflicts left to resolve 👍 Once that's done, feel free to merge.

bors delegate=@Aceeri

@bors
Copy link
Contributor

bors bot commented Nov 2, 2017

✌️ Aceeri can now approve this pull request

Copy link
Member

@torkleyy torkleyy left a comment

Choose a reason for hiding this comment

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

Looks good!

@Aceeri Aceeri force-pushed the entry branch 2 times, most recently from 6a8a53f to 55c6180 Compare November 2, 2017 10:10
@Aceeri
Copy link
Member Author

Aceeri commented Nov 2, 2017

bors r+

bors bot added a commit that referenced this pull request Nov 2, 2017
274: Adds StorageEntry to Storage for convenience r=Aceeri a=Aceeri

Fixes #268 

Adds `StorageEntry` to allow for in place insertions, also some other methods for dealing with the occupied and vacant entries, similar to `std`s HashMap Entry.

Still need to add some tests for further verification (I'll do this tomorrow).
@Aceeri
Copy link
Member Author

Aceeri commented Nov 2, 2017

Should probably add this to the changelog.

bors r-

@bors
Copy link
Contributor

bors bot commented Nov 2, 2017

Canceled

@Aceeri
Copy link
Member Author

Aceeri commented Nov 2, 2017

bors r+

bors bot added a commit that referenced this pull request Nov 2, 2017
274: Adds StorageEntry to Storage for convenience r=Aceeri a=Aceeri

Fixes #268 

Adds `StorageEntry` to allow for in place insertions, also some other methods for dealing with the occupied and vacant entries, similar to `std`s HashMap Entry.

Still need to add some tests for further verification (I'll do this tomorrow).
@bors
Copy link
Contributor

bors bot commented Nov 2, 2017

Build succeeded

@bors bors bot merged commit 313a0a8 into amethyst:master Nov 2, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants