Combine Query::get and Query::get_many#18036
Combine Query::get and Query::get_many#18036chescock wants to merge 10 commits intobevyengine:mainfrom
Query::get and Query::get_many#18036Conversation
IMO we should just deprecate the panicking versions here. We're steadily pushing towards removing this panic-by-default philosophy now that we have system error handling. |
If we're going to deprecate it, we don't need to generalize it.
Sounds good! I updated the PR to deprecate those, as well. |
|
If I've understood the structure correctly, this uses the same Edit: Ah, that is what the read-only bool is for, nevermind! |
| // SAFETY: We checked for duplicates above | ||
| let entities = unsafe { UniqueEntitySlice::from_slice_unchecked(self) }; | ||
| Ok(query.iter_many_unique_inner(entities).collect()) | ||
| } |
There was a problem hiding this comment.
The duplicate check happens only for mutable data, which means we can pass in duplicates when D is immutable!
There was a problem hiding this comment.
Oh, true, I'll switch that to do it the long way like the other methods.
I think I had that before I added the D::IS_READONLY check, and didn't revisit it. It's probably sound in practice, but it's better to have something where we can follow the proof!
alice-i-cecile
left a comment
There was a problem hiding this comment.
Funny to see the constant-in-trait approach again. I'm a bit leery about complicating the call signature of Query::get, but deduplication is a really valuable goal, both for API surface and to avoid soundness bugs. Can you run a quick set of benchmarks before I merge this to ensure we haven't somehow regressed Query::get performance?
| const IS_READ_ONLY: bool; | ||
|
|
||
| /// The read-only variant of this [`QueryData`], which satisfies the [`ReadOnlyQueryData`] trait. | ||
| type ReadOnly: ReadOnlyQueryData<State = <Self as WorldQuery>::State>; |
There was a problem hiding this comment.
Is it possible to bound ReadOnly to require IS_READ_ONLY == true?
There was a problem hiding this comment.
I think it would require the unstable generic_const_exprs feature, since the bounds only take types and you can't currently refer to an associated constant in a type.
I'm happy to update it if someone figures out how to do it! We'd probably want to do the same for QueryFilter::IS_ARCHETYPAL / ArchetypeFilter.
|
I would like to note that reducing API surface isn't necessarily a good thing, but rather a trade-off. I am somewhat iffy about the impls for While this O(n^2) could be replaced with I am also not too keen on the slice -> The I'd also suggest here that we encourage On the other hand, many users of When designing Overall, how to fetch from a That being said, these are my opinions! |
… actually have a `UniqueEntitySlice` in the read-only case.
Yeah, I stole the idea from
Benchmarks always seem a little flaky on my machine, but I think I can do one better and check that the assembly hasn't changed. use crate::prelude::Component;
#[derive(Component)]
struct C(usize);
#[unsafe(no_mangle)]
#[inline(never)]
pub fn call_get(query: Query<&C>, entity: Entity) -> Option<usize> {
Some(query.get(entity).ok()?.0)
}and ran and the resulting assembly was completely identical before or after the change: Details |
Yup, it's definitely a tradeoff! The main thing I find compelling about this approach is that we have several orthogonal axes here, and it lets us use M + N items instead of M * N. In particular, we need I also like that we're able to share a single trait between
I really want to keep these consistent between Maybe what we want is a |
# Conflicts: # crates/bevy_ecs/src/query/mod.rs
|
I think that the increased complexity for an essential beginner API (Query::get) isn't worth deduplicating here. Nominating to close :) I do like the consistency and deduplication in general, I just think it's important to keep the most used APIs as simple as possible. |
Sounds good! As a follow-up, if we like consistency... should we consider getting rid of |
While having less methods is good, combining axes is usually only done when there is one clear conceptual interpretation.
I too would like consistency, and I'd say it is better to restore the separation in the
Yeah, a |
Objective
Reduce the API surface of
Queryby combininggetandget_many, following the pattern in #15614.Solution
Add an associated type and method to the existing
WorldEntityFetchtype that can returnQueryData.Combine
get_many_innerandget_many_readonly. Those differ because we don't need to check for duplicateEntitys for read-only queries. Instead of separate methods, add aQueryData::IS_READ_ONLYassociated constant, and skip the check if the query is read-only. That means callingget_muton a read-only query will not for duplicates!Deprecate the
get_manymethods in favor ofget. Deprecate themanymethods in favor ofget()?orget().unwrap().