-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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
UniqueEntityVec
just 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
Entity
could 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
Entity
could 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 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 acceptEntityBorrow
instead ofEntity
in 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_many
family are the last methods onQuery
/QueryState
for which we're still missing aunique
version.Solution
Offer
get_many_unique
/get_many_unique_mut
andget_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_readonly
intoget_many_inner
and the currentget_many_inner
intoget_many_mut_inner
to clarify their purposes.Testing
Doc examples.
Main Branch Migration Guide
get_many_inner
is now calledget_many_mut_inner
.get_many_readonly
is now calledget_many_inner
.