-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
implement get_many_unique #18315
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
implement get_many_unique #18315
Conversation
crates/bevy_ecs/src/query/state.rs
Outdated
| /// struct A(usize); | ||
| /// | ||
| /// let mut world = World::new(); | ||
| /// let entity_vec: UniqueEntityVec<Entity> = world.spawn_batch((0..3).map(A)).collect_set(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two quick questions if you have the time
- Isn't a
UniqueEntityVec<Entity>kinda redundant? What else would go between the <>? - Isn't a
UniqueEntityVecjust a Set? Why not just call it a set?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean the questions in general? Or just in this specific case?
If its just these lines:
- You're right here, this can probably just be inferred.
- The variable name is more descriptive as
entity_set, yeah!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nevermind, seems like the Entity parameter in 1. doesn't infer!
While Entity could be the type default for UniqueEntityVec, same with UniqueEntitySlice, this isn't easily the case for UniqueEntityArray.
Because type defaults have to be trailing, this would turn UniqueEntityArray<Entity, N> into UniqueEntityArray<N, Entity>, which is quite an ugly inconsistency with [Entity; N].
Not sure what should be done here.
As for what else can go in this parameter, it can entity newtypes like MainEntity/RenderEntity or EntityRef-like types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for responding!
Yeah, this is just a naming nit really. Maybe when the 'unique' functionality is all done it could be polished up in the future.
I think the type UniqueEntityVec<Entity> could maybe be turned into Set<Entity> or even just EntitySet at some point. But I was mostly asking just because I was curious about if there was a reason to call it UniqueEntityVec rather than anything else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While
Entitycould be the type default forUniqueEntityVec, same withUniqueEntitySlice, this isn't easily the case forUniqueEntityArray.
Because type defaults have to be trailing, this would turnUniqueEntityArray<Entity, N>intoUniqueEntityArray<N, Entity>, which is quite an ugly inconsistency with[Entity; N].
Not sure what should be done here.
Oh, adding defaults there is a good idea! I expect Entity to be the most common type by far. The inconsistency with UniqueEntityArray<N, T> and [T; N] is unfortunate, but if the normal case is UniqueEntityArray<N> vs [Entity; N] then it doesn't seem so bad!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While
Entitycould be the type default forUniqueEntityVec, same withUniqueEntitySlice, this isn't easily the case forUniqueEntityArray.
Because type defaults have to be trailing, this would turnUniqueEntityArray<Entity, N>intoUniqueEntityArray<N, Entity>, which is quite an ugly inconsistency with[Entity; N].
Not sure what should be done here.Oh, adding defaults there is a good idea! I expect
Entityto be the most common type by far. The inconsistency withUniqueEntityArray<N, T>and[T; N]is unfortunate, but if the normal case isUniqueEntityArray<N>vs[Entity; N]then it doesn't seem so bad!
Hmm, one way to potentially remedy the inconsistency is by introducing a different inconsistency, which is also a type default for N, f.e. 0, I haven't thought a lot about that yet, but would you think it makes sense?
Edit: Actually, I think Rust is not intelligent enough to allow for UniqueEntityArray<3> when both generics are defaulted, we'd need to mention both, defeating the whole purpose :P
| /// let entities: UniqueEntityVec<Entity> = world.spawn_batch((0..3).map(A)).collect_set(); | ||
| /// let entities: UniqueEntityArray<Entity, 3> = entities.try_into().unwrap(); | ||
| /// | ||
| /// world.spawn(A(73)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are spawning an entity here with component A(73) because you want to make sure that the query_state.get_many_unique_mut call only returns the entities in the UniqueEntityArray<Entity, 3> right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have copied and adjusted the existing doc tests for get_many and get_many_mut for this, so I am not the original writer, but that does seem to be the reason for that spawn.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool beans! I think that an example that showcases this functionality would be good then cause you could comment why world.spawn(A(73)); was there.
Totally not blocking tho!
| /// let invalid_entity = world.spawn_empty().id(); | ||
| /// | ||
| /// assert_eq!(match query_state.get_many_unique(&mut world, UniqueEntityArray::from([wrong_entity])).unwrap_err() {QueryEntityError::EntityDoesNotExist(error) => error.entity, _ => panic!()}, wrong_entity); | ||
| /// assert_eq!(match query_state.get_many_unique_mut(&mut world, UniqueEntityArray::from([invalid_entity])).unwrap_err() {QueryEntityError::QueryDoesNotMatch(entity, _) => entity, _ => panic!()}, invalid_entity); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the reason this is returning an error because you expect every entity to have component 'A'? If so, why not just return an empty list of components?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These asserts test that the error cases are produced properly.
If an entity does not match the D in QueryData, that should return an error!
Same with an entity that does not exist in the first place.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like it! I would love an example for how to use unique entity lists and why it might be useful!
Once I am done with the feature work I'll have a look throughout the engine for where they can improve things :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good!
I think get_many_inner and get_many_readonly are new in 0.16, so if we can get this in before the release is cut then we won't need the migration guide.
| /// assert_eq!(match query_state.get_many_unique(&mut world, UniqueEntityArray::from([wrong_entity])).unwrap_err() {QueryEntityError::EntityDoesNotExist(error) => error.entity, _ => panic!()}, wrong_entity); | ||
| /// ``` | ||
| #[inline] | ||
| pub fn get_many_unique<'w, const N: usize>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm hoping to deprecate the methods like this on QueryState, so I'd mildly prefer not to add these in the first place. The functionality would still be available as .query(world).get_many_unique_inner(entities).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I was a little confused why this function needed to be implemented for both the state module and query module. It seems like it would only really be needed in the query module
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I was a little confused why this function needed to be implemented for both the state module and query module.
For context: Until recently, the actual implementation of all of these methods was on QueryState, and the Query methods just called those. I added the QueryState::query methods and moved the implementations over, with the eventual goal of getting rid of most of the other methods on QueryState.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am aware that we're trying to deprecate them!
However in the spirit of being incremental, I find it more confusing to not add them here when the other methods do not yet have the #[deprecated] moniker. Once we do deprecate them before 0.16, then feel free to remove these again!
Given that sometimes PRs can miss a cut-off, or contributors don't have time before an RC/release, leaving changes unfinished seems like an uneccessary risk, when both adding and removing is simple implementation-wise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in the spirit of being incremental, I find it more confusing to not add them here
Well, in that case, you forgot get_many_unique_manual(&self, &World, entities) and get_many_unique_unchecked(&mut self, UnsafeWorldCell, entities) :P
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in the spirit of being incremental, I find it more confusing to not add them here
Well, in that case, you forgot
get_many_unique_manual(&self, &World, entities)andget_many_unique_unchecked(&mut self, UnsafeWorldCell, entities):P
These already don't exist for get_many, so I haven't added them here.
These inconsistencies are not nice, but we can at least not make them worse in the interim :P
crates/bevy_ecs/src/system/query.rs
Outdated
| #[inline] | ||
| pub fn get_many_unique_inner<const N: usize>( | ||
| self, | ||
| entities: UniqueEntityArray<Entity, N>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to generalize this from Entity to T: TrustedEntityBorrow? (And I suppose the read-only one could be generalized to T: EntityBorrow.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, we could generalize them!
However, this also goes for a lot more places than just these methods.
The general decision of "We want to accept EntityBorrow instead of Entity in our APIs" is contentious, so I'd like to leave that for when the EntitySet-family of features is done.
(With iter_many, Borrow<Entity> was already present)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However, this also goes for a lot more places than just these methods.
The general decision of "We want to acceptEntityBorrowinstead ofEntityin our APIs" is contentious, so I'd like to leave that for when theEntitySet-family of features is done.
Oh, I remember waffling about this when writing #18234. Checking now, it looks like I left a few as T: TrustedEntityBorrow. Should I switch those back to Entity, too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you? I don't see them!
The current status quo is:
iter_many: takes IntoIterator<Item: EntityBorrow>
iter_many_unique takes EntitySet
The iter_many signatures are currently the only signatures where we use EntityBorrow, and wherever we use EntitySet we have a hidden TrustedEntityBorrow bound.
Otherwise, the EntityBorrow and TrustedEntityBorrow traits shouldn't appear in non-EntitySet related methods for now I think.
Objective
Continuation to #16547 and #17954.
The
get_manyfamily are the last methods onQuery/QueryStatefor which we're still missing auniqueversion.Solution
Offer
get_many_unique/get_many_unique_mutandget_many_unique_inner!Their implementation is the same as
get_many, the difference lies in their guaranteed-to-be unique inputs, meaning we never do any aliasing checks.To reduce confusion, we also rename
get_many_readonlyintoget_many_innerand the currentget_many_innerintoget_many_mut_innerto clarify their purposes.Testing
Doc examples.
Main Branch Migration Guide
get_many_inneris now calledget_many_mut_inner.get_many_readonlyis now calledget_many_inner.