-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
add a QueryManyUniqueIter #13477
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
add a QueryManyUniqueIter #13477
Conversation
f4f433e
to
cabaf73
Compare
# Objective The current query iterators cannot be used in positions with a `Debug` bound. F.e. when they are packaged in `Result` in the error position, `expect` cannot be called on them. Required for `QueryManyIter::entities_all_unique` in #13477. ## Solution Add simple `Debug` impls that print the query iterator names. ## Changelog `QueryIter`, `QueryManyIter`, `QueryCombinationIter`, and `QuerySortedIter` now implement `Debug`.
cabaf73
to
3c502c4
Compare
crates/bevy_ecs/src/query/iter.rs
Outdated
{ | ||
// Entities are guaranteed to be unique, so no lifetime shrinking needed for mutable iteration. | ||
#[inline(always)] | ||
fn fetch_next(&mut self) -> Option<D::Item<'w>> { |
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.
This duplicates the code in QueryManyIter::fetch_next_aliased_unchecked()
. Could you share the implementation by having QueryManyUniqueIter
wrap a whole QueryManyIter
, and then implement Iterator::next()
by calling fetch_next_aliased_unchecked()
on the inner QueryManyIter
?
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.
IIRC, I was not sure whether the uniqueness property could simplify the next()
call, so I kept them separate, but looking at it again, you're right, this can just be wrapper type! Good idea.
/// # schedule.run(&mut world); | ||
/// ``` | ||
#[inline(always)] | ||
pub fn entities_all_unique( |
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.
It might be useful to have methods to construct a QueryManyUniqueIter
directly from a few iterator types that can already guarantee uniqueness, such as HashSet
. That could let users skip the overhead of the uniqueness check.
It might also be useful to have a method that removes duplicates instead of returning Err
, by doing something like
entity_iter: self
.entity_iter
.map(|e| *e.borrow())
.collect::<EntityHashSet>()
.into_iter(),
(Those could always be done in future PRs, of course.)
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 agree with the methods to construct from unique iterators. What other source iterator types would you suggest?
Deduplication can already be done on the iterator itself with itertools
, so isn't as important to have as the others. Still a nice to have though.
I wonder whether a sort + dedup would be faster than your HashSet
version.
Edit: Actually just collecting into a BTreeSet
might be simpler and faster. None of these retain input order however, that would be more expensive.
I think I'll make these separate follow-up PRs, since I have to keep these changes in line with #13443.
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 added dedup_entities
on QueryManyIter
to this PR anyway.
also adds: a Debug impl associated type bounds in the struct and impl definitions a missing unsafe block in QueryManyIter
.get(location.archetype_id) | ||
.debug_checked_unwrap(); | ||
let table = self.tables.get(location.table_id).debug_checked_unwrap(); | ||
let (archetype, table); |
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.
This is good cleanup, but could probably be a separate PR.
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.
Fair, I'll put it in a different PR.
I wonder whether it might be easier to have a direct |
I have come up with a more powerful, general solution, which I think I will put in a different PR. I'll keep this open as the simpler alternative.
|
This PR is now superseded by the concept of #16547, though the actual conversion API would be a follow-up to that PR. |
#16547 has merged. |
Objective
Requires #13476.Currently we cannot iterate mutably over
QueryManyIter
even if all entities we pass are unique.Solution
Add a
QueryManyUniqueIter
wrapper aroundQueryManyIter
for fullIterator
trait iteration.It can be constructed via either the
dedup_entities
orentities_all_unique
method onQueryManyIter
.dedup_entities
deduplicates the internalentity_iter
, retaining order.entities_all_unique
takesself
, checks for uniqueness, returnsQueryManyUniqueIter
on success, andQueryManyIter
(Self
) on failure.Since
QueryManyUniqueIter
can iterate over mutableQueryData
, it does not expose afetch_next
method.Testing
The example/doc test shows the method in use, also making sure
expect
can be called on the returnResult
.Changelog
Added
dedup_entities
andentities_all_unique
methods onQueryManyIter
.Added
QueryManyUniqueIter
struct.