Streaming iteration for non-IterQueryData (#23218)

# Objective

Follow-up to #21557

Support streaming iteration (a.k.a. lending iteration) for `QueryIter`
and `QuerySortedIter` so that it's possible to iterate
`Query<Parent<&mut T>>`.

Double-check that all the `IterQueryData` bounds are correct and that
the safety comments are correct. A few were not!

## Solution

Offer a `fetch_next` method on `QueryIter` and `QuerySortedIter` that
borrows the entire iterator to prevent aliasing, similar to
`QueryManyIter::QueryManyIter`.

Offer a `Query::iter_inner` method to produce `QueryIter` even when it
is not an `Iterator`. We cannot rely on `IntoIterator` in that case, and
Clippy objects to an inherent method called `into_iter()` that is not
the standard trait method. This supersedes the existing `iter_inner`
method that only worked on `ReadOnlyQueryData`.

Add a missing `IterQueryData` bound to `iter_combinations_mut`. It
yields multiple entities concurrently, so is never sound to use with
non-`IterQueryData`. I think the reason I missed this originally is that
it already has a `fetch_next` method and conditional `Iterator` impl.
But even `fetch_next` yields multiple entities at once for
`QueryCombinationIter`!

Add a `ContiguousQueryData: IterQueryData` supertrait bound.
`QueryContiguousIter` also yields multiple entities concurrently, so it
does not make sense on non-iterable data. This was not actually unsound,
though, as `QueryContiguousIter` does not call `fetch`.

Finally, update some missing or incorrect safety comments.  

### Verify bounds on the other iterator types

To verify I didn't miss any bounds on iterator types, here is a list of
every `Query*Iter` type, and whether they support non-`IterQueryData`:

| Type | Supports non-`Iter`? |
| --- | --- |
|`CombinationIter`|No, added missing bound|
|`ContiguousIter`|No, added missing bound|
|`Iter`|Yes, added support|
|`ManyIter`|Yes, existing support|
|`ManyUniqueIter`|No, and existing bound*|
|`ParIter`|No, and existing bound|
|`ParManyIter`|No, and existing bound|
|`ParManyUniqueIter`|No, and existing bound|
|`SortedIter`|Yes, added support|
|`SortedManyIter`|Yes, existing support|

`Iter` and `SortedIter` were changed in this PR to support streaming
iteration for all `QueryData`, while only implementing `Iterator` for
`IterQueryData`.

`ManyIter` and `SortedManyIter` already had streaming iteration, and
only implement `Iterator` with a stricter `ReadOnlyQueryData` bound,
since they may yield the same entity multiple times.

`ManyUniqueIter` could theoretically be used with non-`IterQueryData`...
but if you need to do streaming iteration anyway then you can call
`iter_many_mut()` instead of `iter_many_unique_mut()`.

`CombinationIter`, `ContiguousIter`, and the `Par*Iter` types are all
fundamentally about accessing multiple entities concurrently, and should
always require `IterQueryData`. `CombinationIter` and `ContiguousIter`
did not have `IterQueryData` bounds, so fix that!

## Showcase

This is cheating a bit because we don't yet have `Parent<&mut T>`, but
once we do it would look like:

```rust
fn system(query: Query<Parent<&mut Component>>) {
    let mut iter = query.iter_mut();
    while let Some(mut parent_component) = iter.fetch_next() {
        // Use `parent_component` here
    }
}
```
This commit is contained in:
Chris Russell
2026-03-11 14:19:11 -04:00
committed by GitHub
parent 2b760d979f
commit 22d7c65403
5 changed files with 289 additions and 83 deletions
+4 -1
View File
@@ -406,7 +406,7 @@ pub unsafe trait QueryData: WorldQuery {
label = "invalid contiguous `Query` data",
note = "if `{Self}` is a custom query type, using `QueryData` derive macro, ensure that the `#[query_data(contiguous(target))]` attribute is added"
)]
pub trait ContiguousQueryData: ArchetypeQueryData {
pub trait ContiguousQueryData: ArchetypeQueryData + IterQueryData {
/// Item returned by [`ContiguousQueryData::fetch_contiguous`].
/// Represents a contiguous chunk of memory.
type Contiguous<'w, 's>;
@@ -434,6 +434,9 @@ pub trait ContiguousQueryData: ArchetypeQueryData {
/// This is how methods like [`Iterator::collect`] work.
/// It is therefore unsound to offer an [`Iterator`] for a [`QueryData`] for which only one instance may be alive concurrently.
///
/// To iterate over a [`QueryData`] that does not implement [`IterQueryData`],
/// use the [`QueryIter::fetch_next()`](crate::query::QueryIter::fetch_next) method.
///
/// For `QueryData` that implement this trait, [`QueryData::fetch`] may be called for one entity while an item is still alive for a different entity.
///
/// All [`SingleEntityQueryData`] types are [`IterQueryData`].
+192 -22
View File
@@ -128,6 +128,44 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
cursor: self.cursor.reborrow(),
}
}
/// Get the next result from the query.
///
/// If the [`QueryData`] does not implement [`IterQueryData`],
/// then it is not sound to yield multiple items concurrently
/// and the resulting [`QueryIter`] will not implement [`Iterator`].
/// In that case, this method can be used to iterate over the items
/// while ensuring only one is alive at a time.
///
/// Most queries do implement [`IterQueryData`],
/// and and can use the ordinary [`Iterator::next`]
/// method or a `for` loop.
///
/// # Example
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # #[derive(Component)]
/// # struct C;
/// fn system(mut query: Query<&mut C>) {
/// let mut iter = query.iter_mut();
/// while let Some(mut c) = iter.fetch_next() {
/// //
/// }
/// }
/// # bevy_ecs::system::assert_is_system(system);
/// ```
pub fn fetch_next(&mut self) -> Option<D::Item<'_, 's>> {
// SAFETY:
// - `tables` and `archetypes` belong to the same world that the cursor was initialized for.
// - `query_state` is the state that was passed to `QueryIterationCursor::init`.
// - `self` is mutably borrowed, so there are no other items alive for any entity.
unsafe {
self.cursor
.next(self.tables, self.archetypes, self.query_state)
.map(D::shrink)
}
}
}
impl<'w, 's, D: IterQueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
@@ -428,6 +466,13 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
///
/// The sort is not cached across system runs.
///
/// If the [`QueryData`] does not implement [`IterQueryData`],
/// then it is not sound to yield multiple items concurrently
/// and the resulting [`QuerySortedIter`] will not implement [`Iterator`].
/// To iterate over the items in that case,
/// use the [`QuerySortedIter::fetch_next()`](crate::query::QuerySortedIter::fetch_next) method,
/// which ensures only one item is alive at a time.
///
/// [allowed transmutes]: crate::system::Query#allowed-transmutes
///
/// # Panics
@@ -559,6 +604,13 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
///
/// The sort is not cached across system runs.
///
/// If the [`QueryData`] does not implement [`IterQueryData`],
/// then it is not sound to yield multiple items concurrently
/// and the resulting [`QuerySortedIter`] will not implement [`Iterator`].
/// To iterate over the items in that case,
/// use the [`QuerySortedIter::fetch_next()`](crate::query::QuerySortedIter::fetch_next) method,
/// which ensures only one item is alive at a time.
///
/// [allowed transmutes]: crate::system::Query#allowed-transmutes
///
/// # Panics
@@ -620,6 +672,13 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
///
/// The sort is not cached across system runs.
///
/// If the [`QueryData`] does not implement [`IterQueryData`],
/// then it is not sound to yield multiple items concurrently
/// and the resulting [`QuerySortedIter`] will not implement [`Iterator`].
/// To iterate over the items in that case,
/// use the [`QuerySortedIter::fetch_next()`](crate::query::QuerySortedIter::fetch_next) method,
/// which ensures only one item is alive at a time.
///
/// [allowed transmutes]: crate::system::Query#allowed-transmutes
///
/// # Panics
@@ -688,6 +747,13 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
///
/// The sort is not cached across system runs.
///
/// If the [`QueryData`] does not implement [`IterQueryData`],
/// then it is not sound to yield multiple items concurrently
/// and the resulting [`QuerySortedIter`] will not implement [`Iterator`].
/// To iterate over the items in that case,
/// use the [`QuerySortedIter::fetch_next()`](crate::query::QuerySortedIter::fetch_next) method,
/// which ensures only one item is alive at a time.
///
/// [allowed transmutes]: crate::system::Query#allowed-transmutes
///
/// # Panics
@@ -724,6 +790,13 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
///
/// The sort is not cached across system runs.
///
/// If the [`QueryData`] does not implement [`IterQueryData`],
/// then it is not sound to yield multiple items concurrently
/// and the resulting [`QuerySortedIter`] will not implement [`Iterator`].
/// To iterate over the items in that case,
/// use the [`QuerySortedIter::fetch_next()`](crate::query::QuerySortedIter::fetch_next) method,
/// which ensures only one item is alive at a time.
///
/// [allowed transmutes]: crate::system::Query#allowed-transmutes
///
/// # Panics
@@ -821,6 +894,13 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
///
/// The sort is not cached across system runs.
///
/// If the [`QueryData`] does not implement [`IterQueryData`],
/// then it is not sound to yield multiple items concurrently
/// and the resulting [`QuerySortedIter`] will not implement [`Iterator`].
/// To iterate over the items in that case,
/// use the [`QuerySortedIter::fetch_next()`](crate::query::QuerySortedIter::fetch_next) method,
/// which ensures only one item is alive at a time.
///
/// [allowed transmutes]: crate::system::Query#allowed-transmutes
///
/// # Panics
@@ -860,6 +940,13 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
///
/// The sort is not cached across system runs.
///
/// If the [`QueryData`] does not implement [`IterQueryData`],
/// then it is not sound to yield multiple items concurrently
/// and the resulting [`QuerySortedIter`] will not implement [`Iterator`].
/// To iterate over the items in that case,
/// use the [`QuerySortedIter::fetch_next()`](crate::query::QuerySortedIter::fetch_next) method,
/// which ensures only one item is alive at a time.
///
/// [allowed transmutes]: crate::system::Query#allowed-transmutes
///
/// # Panics
@@ -894,6 +981,13 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
///
/// The sort is not cached across system runs.
///
/// If the [`QueryData`] does not implement [`IterQueryData`],
/// then it is not sound to yield multiple items concurrently
/// and the resulting [`QuerySortedIter`] will not implement [`Iterator`].
/// To iterate over the items in that case,
/// use the [`QuerySortedIter::fetch_next()`](crate::query::QuerySortedIter::fetch_next) method,
/// which ensures only one item is alive at a time.
///
/// [allowed transmutes]: crate::system::Query#allowed-transmutes
///
/// # Panics
@@ -956,8 +1050,9 @@ impl<'w, 's, D: IterQueryData, F: QueryFilter> Iterator for QueryIter<'w, 's, D,
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
// SAFETY:
// `tables` and `archetypes` belong to the same world that the cursor was initialized for.
// `query_state` is the state that was passed to `QueryIterationCursor::init`.
// - `tables` and `archetypes` belong to the same world that the cursor was initialized for.
// - `query_state` is the state that was passed to `QueryIterationCursor::init`.
// - `D: IterQueryData`
unsafe {
self.cursor
.next(self.tables, self.archetypes, self.query_state)
@@ -1170,13 +1265,77 @@ where
}
}
/// Get the next result from the query.
///
/// If the [`QueryData`] does not implement [`IterQueryData`],
/// then it is not sound to yield multiple items concurrently
/// and the resulting [`QuerySortedIter`] will not implement [`Iterator`].
/// In that case, this method can be used to iterate over the items
/// while ensuring only one is alive at a time.
///
/// Most queries do implement [`IterQueryData`],
/// and and can use the ordinary [`Iterator::next`]
/// method or a `for` loop.
///
/// # Example
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # #[derive(Component, Ord, PartialOrd, Eq, PartialEq)]
/// # struct C;
/// fn system(mut query: Query<&mut C>) {
/// let mut iter = query.iter_mut().sort::<&C>();
/// while let Some(mut c) = iter.fetch_next() {
/// //
/// }
/// }
/// # bevy_ecs::system::assert_is_system(system);
/// ```
pub fn fetch_next(&mut self) -> Option<D::Item<'_, 's>> {
while let Some(entity) = self.entity_iter.next() {
// SAFETY:
// - `entity` is passed from `entity_iter` the first time.
// - `self` is mutably borrowed, so there are no other items alive for any entity.
if let Some(item) = unsafe { self.fetch_next_impl(entity) } {
return Some(D::shrink(item));
}
}
None
}
/// Get the next result from the back of the query.
///
/// If the [`QueryData`] does not implement [`IterQueryData`],
/// then it is not sound to yield multiple items concurrently
/// and the resulting [`QuerySortedIter`] will not implement [`Iterator`].
/// In that case, this method can be used to iterate over the items
/// while ensuring only one is alive at a time.
///
/// Most queries do implement [`IterQueryData`],
/// and and can use the ordinary [`Iterator::next`]
/// method or a `for` loop.
pub fn fetch_next_back(&mut self) -> Option<D::Item<'_, 's>>
where
I: DoubleEndedIterator,
{
while let Some(entity) = self.entity_iter.next_back() {
// SAFETY:
// - `entity` is passed from `entity_iter` the first time.
// - `self` is mutably borrowed, so there are no other items alive for any entity.
if let Some(item) = unsafe { self.fetch_next_impl(entity) } {
return Some(D::shrink(item));
}
}
None
}
/// # Safety
///
/// - `entity` must stem from `self.entity_iter`
/// - If `Self` does not impl `ReadOnlyQueryData`, then there must not be any other `Item`s alive for the current entity
/// - If `Self` does not impl `IterQueryData`, then there must not be any other `Item`s alive for *any* entity
/// - If `D` does not impl `ReadOnlyQueryData`, then there must not be any other `Item`s alive for the current entity
/// - If `D` does not impl `IterQueryData`, then there must not be any other `Item`s alive for *any* entity
#[inline(always)]
unsafe fn fetch_next(&mut self, entity: Entity) -> Option<D::Item<'w, 's>> {
unsafe fn fetch_next_impl(&mut self, entity: Entity) -> Option<D::Item<'w, 's>> {
let (location, archetype, table);
// SAFETY:
// `tables` and `archetypes` belong to the same world that the [`QueryIter`]
@@ -1229,7 +1388,7 @@ where
// SAFETY:
// - `entity` is passed from `entity_iter` the first time.
// - `D: IterQueryData`
if let Some(item) = unsafe { self.fetch_next(entity) } {
if let Some(item) = unsafe { self.fetch_next_impl(entity) } {
return Some(item);
}
}
@@ -1255,7 +1414,7 @@ where
// SAFETY:
// - `entity` is passed from `entity_iter` the first time.
// - `D: IterQueryData`
if let Some(item) = unsafe { self.fetch_next(entity) } {
if let Some(item) = unsafe { self.fetch_next_impl(entity) } {
return Some(item);
}
}
@@ -1345,8 +1504,8 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item: EntityEquivalent>>
/// # Safety
///
/// - All arguments must stem from the same valid `QueryManyIter`.
/// - If `Self` does not impl `ReadOnlyQueryData`, then there must not be any other `Item`s alive for the current entity
/// - If `Self` does not impl `IterQueryData`, then there must not be any other `Item`s alive for *any* entity
/// - If `D` does not impl `ReadOnlyQueryData`, then there must not be any other `Item`s alive for the current entity
/// - If `D` does not impl `IterQueryData`, then there must not be any other `Item`s alive for *any* entity
#[inline(always)]
unsafe fn fetch_next_aliased_unchecked(
entity_iter: impl Iterator<Item: EntityEquivalent>,
@@ -1408,7 +1567,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item: EntityEquivalent>>
None
}
/// Get next result from the query
/// Get the next result from the query
#[inline(always)]
pub fn fetch_next(&mut self) -> Option<D::Item<'_, 's>> {
// SAFETY:
@@ -1949,7 +2108,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item: EntityEquivalent>>
impl<'w, 's, D: QueryData, F: QueryFilter, I: DoubleEndedIterator<Item: EntityEquivalent>>
QueryManyIter<'w, 's, D, F, I>
{
/// Get next result from the back of the query
/// Get the next result from the back of the query
#[inline(always)]
pub fn fetch_next_back(&mut self) -> Option<D::Item<'_, 's>> {
// SAFETY:
@@ -2181,8 +2340,8 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item = Entity>>
/// # Safety
///
/// - `entity` must stem from `self.entity_iter`
/// - If `Self` does not impl `ReadOnlyQueryData`, then there must not be any other `Item`s alive for the current entity
/// - If `Self` does not impl `IterQueryData`, then there must not be any other `Item`s alive for *any* entity
/// - If `D` does not impl `ReadOnlyQueryData`, then there must not be any other `Item`s alive for the current entity
/// - If `D` does not impl `IterQueryData`, then there must not be any other `Item`s alive for *any* entity
#[inline(always)]
unsafe fn fetch_next_aliased_unchecked(&mut self, entity: Entity) -> Option<D::Item<'w, 's>> {
let (location, archetype, table);
@@ -2223,7 +2382,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item = Entity>>
}
}
/// Get next result from the query
/// Get the next result from the query
#[inline(always)]
pub fn fetch_next(&mut self) -> Option<D::Item<'_, 's>> {
while let Some(entity) = self.entity_iter.next() {
@@ -2245,7 +2404,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item = Entity>>
impl<'w, 's, D: QueryData, F: QueryFilter, I: DoubleEndedIterator<Item = Entity>>
QuerySortedManyIter<'w, 's, D, F, I>
{
/// Get next result from the query
/// Get the next result from the query
#[inline(always)]
pub fn fetch_next_back(&mut self) -> Option<D::Item<'_, 's>> {
while let Some(entity) = self.entity_iter.next_back() {
@@ -2386,14 +2545,16 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item = Entity>> Debug
/// [`Query`]: crate::system::Query
/// [`Query::iter_combinations`]: crate::system::Query::iter_combinations
/// [`Query::iter_combinations_mut`]: crate::system::Query::iter_combinations_mut
pub struct QueryCombinationIter<'w, 's, D: QueryData, F: QueryFilter, const K: usize> {
pub struct QueryCombinationIter<'w, 's, D: IterQueryData, F: QueryFilter, const K: usize> {
tables: &'w Tables,
archetypes: &'w Archetypes,
query_state: &'s QueryState<D, F>,
cursors: [QueryIterationCursor<'w, 's, D, F>; K],
}
impl<'w, 's, D: QueryData, F: QueryFilter, const K: usize> QueryCombinationIter<'w, 's, D, F, K> {
impl<'w, 's, D: IterQueryData, F: QueryFilter, const K: usize>
QueryCombinationIter<'w, 's, D, F, K>
{
/// # Safety
/// - `world` must have permission to access any of the components registered in `query_state`.
/// - `world` must be the same one used to initialize `query_state`.
@@ -2552,7 +2713,7 @@ impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, const K: usize> FusedIterator
{
}
impl<'w, 's, D: QueryData, F: QueryFilter, const K: usize> Debug
impl<'w, 's, D: IterQueryData, F: QueryFilter, const K: usize> Debug
for QueryCombinationIter<'w, 's, D, F, K>
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
@@ -2647,7 +2808,10 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
/// The result of `next` and any previous calls to `peek_last` with this row must have been
/// dropped to prevent aliasing mutable references.
#[inline]
unsafe fn peek_last(&mut self, query_state: &'s QueryState<D, F>) -> Option<D::Item<'w, 's>> {
unsafe fn peek_last(&mut self, query_state: &'s QueryState<D, F>) -> Option<D::Item<'w, 's>>
where
D: IterQueryData,
{
if self.current_row > 0 {
let index = self.current_row - 1;
if self.is_dense {
@@ -2656,6 +2820,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
// SAFETY:
// - `set_table` must have been called previously either in `next` or before it.
// - `*entity` and `index` are in the current table.
// - `D: IterQueryData`
unsafe {
D::fetch(
&query_state.fetch_state,
@@ -2672,6 +2837,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
// SAFETY:
// - `set_archetype` must have been called previously either in `next` or before it.
// - `archetype_entity.id()` and `archetype_entity.table_row()` are in the current archetype.
// - `D: IterQueryData`
unsafe {
D::fetch(
&query_state.fetch_state,
@@ -2707,9 +2873,11 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
// QueryState::par_fold_init_unchecked_manual, QueryState::par_many_fold_init_unchecked_manual,
// QueryState::par_many_unique_fold_init_unchecked_manual, QueryContiguousIter::next
/// # Safety
/// `tables` and `archetypes` must belong to the same world that the [`QueryIterationCursor`]
/// was initialized for.
/// `query_state` must be the same [`QueryState`] that was passed to `init` or `init_empty`.
/// - `tables` and `archetypes` must belong to the same world that the [`QueryIterationCursor`]
/// was initialized for.
/// - `query_state` must be the same [`QueryState`] that was passed to `init` or `init_empty`.
/// - If `D` does not impl `ReadOnlyQueryData`, then there must not be any other `Item`s alive for the current entity
/// - If `D` does not impl `IterQueryData`, then there must not be any other `Item`s alive for *any* entity
#[inline(always)]
unsafe fn next(
&mut self,
@@ -2756,6 +2924,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
// - `current_row` must be a table row in range of the current table,
// because if it was not, then the above would have been executed.
// - fetch is only called once for each `entity`.
// - caller ensures no conflicting `Item`s are alive
let item =
unsafe { D::fetch(&query_state.fetch_state, &mut self.fetch, *entity, row) };
if let Some(item) = item {
@@ -2814,6 +2983,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
// - `current_row` must be an archetype index row in range of the current archetype,
// because if it was not, then the if above would have been executed.
// - fetch is only called once for each `archetype_entity`.
// - caller ensures no conflicting `Item`s are alive
let item = unsafe {
D::fetch(
&query_state.fetch_state,
+13 -13
View File
@@ -1174,7 +1174,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
/// consider using [`Self::update_archetypes`] followed by multiple [`Self::iter_manual`] calls.
#[inline]
pub fn iter<'w, 's>(&'s mut self, world: &'w World) -> QueryIter<'w, 's, D::ReadOnly, F> {
self.query(world).into_iter()
self.query(world).iter_inner()
}
/// Returns an [`Iterator`] over the query results for the given [`World`].
@@ -1182,11 +1182,8 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
/// This iterator is always guaranteed to return results from each matching entity once and only once.
/// Iteration order is not guaranteed.
#[inline]
pub fn iter_mut<'w, 's>(&'s mut self, world: &'w mut World) -> QueryIter<'w, 's, D, F>
where
D: IterQueryData,
{
self.query_mut(world).into_iter()
pub fn iter_mut<'w, 's>(&'s mut self, world: &'w mut World) -> QueryIter<'w, 's, D, F> {
self.query_mut(world).iter_inner()
}
/// Returns an [`Iterator`] over the query results for the given [`World`] without updating the query's archetypes.
@@ -1254,7 +1251,10 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
pub fn iter_combinations_mut<'w, 's, const K: usize>(
&'s mut self,
world: &'w mut World,
) -> QueryCombinationIter<'w, 's, D, F, K> {
) -> QueryCombinationIter<'w, 's, D, F, K>
where
D: IterQueryData,
{
self.query_mut(world).iter_combinations_inner()
}
@@ -1383,12 +1383,9 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
pub unsafe fn iter_unchecked<'w, 's>(
&'s mut self,
world: UnsafeWorldCell<'w>,
) -> QueryIter<'w, 's, D, F>
where
D: IterQueryData,
{
) -> QueryIter<'w, 's, D, F> {
// SAFETY: Upheld by caller
unsafe { self.query_unchecked(world) }.into_iter()
unsafe { self.query_unchecked(world) }.iter_inner()
}
/// Returns an [`Iterator`] over all possible combinations of `K` query results for the
@@ -1406,7 +1403,10 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
pub unsafe fn iter_combinations_unchecked<'w, 's, const K: usize>(
&'s mut self,
world: UnsafeWorldCell<'w>,
) -> QueryCombinationIter<'w, 's, D, F, K> {
) -> QueryCombinationIter<'w, 's, D, F, K>
where
D: IterQueryData,
{
// SAFETY: Upheld by caller
unsafe { self.query_unchecked(world) }.iter_combinations_inner()
}
+68 -45
View File
@@ -684,6 +684,13 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
/// This iterator is always guaranteed to return results from each matching entity once and only once.
/// Iteration order is not guaranteed.
///
/// If the [`QueryData`] does not implement [`IterQueryData`],
/// then it is not sound to yield multiple items concurrently
/// and the resulting [`QueryIter`] will not implement [`Iterator`].
/// To iterate over the items in that case,
/// use the [`QueryIter::fetch_next()`](crate::query::QueryIter::fetch_next) method,
/// which ensures only one item is alive at a time.
///
/// # Example
///
/// Here, the `gravity_system` updates the `Velocity` component of every entity that contains it:
@@ -706,11 +713,47 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
///
/// [`iter`](Self::iter) for read-only query items.
#[inline]
pub fn iter_mut(&mut self) -> QueryIter<'_, 's, D, F>
where
D: IterQueryData,
{
self.reborrow().into_iter()
pub fn iter_mut(&mut self) -> QueryIter<'_, 's, D, F> {
self.reborrow().iter_inner()
}
/// Returns an [`Iterator`] over the query items, with the actual "inner" world lifetime.
///
/// This iterator is always guaranteed to return results from each matching entity once and only once.
/// Iteration order is not guaranteed.
///
/// If the [`QueryData`] does not implement [`IterQueryData`],
/// then it is not sound to yield multiple items concurrently
/// and the resulting [`QueryIter`] will not implement [`Iterator`].
/// To iterate over the items in that case,
/// use the [`QueryIter::fetch_next()`](crate::query::QueryIter::fetch_next) method,
/// which ensures only one item is alive at a time.
///
/// # Example
///
/// Here, the `report_names_system` iterates over the `Player` component of every entity
/// that contains it:
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(Component)]
/// # struct Player { name: String }
/// #
/// fn report_names_system(query: Query<&Player>) {
/// for player in &query {
/// println!("Say hello to {}!", player.name);
/// }
/// }
/// # bevy_ecs::system::assert_is_system(report_names_system);
/// ```
#[inline]
pub fn iter_inner(self) -> QueryIter<'w, 's, D, F> {
// SAFETY:
// - `self.world` has permission to access the required components.
// - We consume the query, so mutable queries cannot alias.
// Read-only queries are `Copy`, but may alias themselves.
unsafe { QueryIter::new(self.world, self.state, self.last_run, self.this_run) }
}
/// Returns a [`QueryCombinationIter`] over all combinations of `K` read-only query items without repetition.
@@ -767,9 +810,10 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
/// - [`iter_combinations`](Self::iter_combinations) for read-only query item combinations.
/// - [`iter_combinations_inner`](Self::iter_combinations_inner) for mutable query item combinations with the full `'world` lifetime.
#[inline]
pub fn iter_combinations_mut<const K: usize>(
&mut self,
) -> QueryCombinationIter<'_, 's, D, F, K> {
pub fn iter_combinations_mut<const K: usize>(&mut self) -> QueryCombinationIter<'_, 's, D, F, K>
where
D: IterQueryData,
{
self.reborrow().iter_combinations_inner()
}
@@ -798,7 +842,10 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
/// - [`iter_combinations`](Self::iter_combinations) for read-only query item combinations.
/// - [`iter_combinations_mut`](Self::iter_combinations_mut) for mutable query item combinations.
#[inline]
pub fn iter_combinations_inner<const K: usize>(self) -> QueryCombinationIter<'w, 's, D, F, K> {
pub fn iter_combinations_inner<const K: usize>(self) -> QueryCombinationIter<'w, 's, D, F, K>
where
D: IterQueryData,
{
// SAFETY: `self.world` has permission to access the required components.
unsafe { QueryCombinationIter::new(self.world, self.state, self.last_run, self.this_run) }
}
@@ -1106,6 +1153,13 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
/// This iterator is always guaranteed to return results from each matching entity once and only once.
/// Iteration order is not guaranteed.
///
/// If the [`QueryData`] does not implement [`IterQueryData`],
/// then it is not sound to yield multiple items concurrently
/// and the resulting [`QueryIter`] will not implement [`Iterator`].
/// To iterate over the items in that case,
/// use the [`QueryIter::fetch_next()`](crate::query::QueryIter::fetch_next) method,
/// which ensures only one item is alive at a time.
///
/// # Safety
///
/// This function makes it possible to violate Rust's aliasing guarantees.
@@ -1139,7 +1193,10 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
#[inline]
pub unsafe fn iter_combinations_unsafe<const K: usize>(
&self,
) -> QueryCombinationIter<'_, 's, D, F, K> {
) -> QueryCombinationIter<'_, 's, D, F, K>
where
D: IterQueryData,
{
// SAFETY: The caller promises that this will not result in multiple mutable references.
unsafe { self.reborrow_unsafe() }.iter_combinations_inner()
}
@@ -2668,11 +2725,7 @@ impl<'w, 's, D: IterQueryData, F: QueryFilter> IntoIterator for Query<'w, 's, D,
type IntoIter = QueryIter<'w, 's, D, F>;
fn into_iter(self) -> Self::IntoIter {
// SAFETY:
// - `self.world` has permission to access the required components.
// - We consume the query, so mutable queries cannot alias.
// Read-only queries are `Copy`, but may alias themselves.
unsafe { QueryIter::new(self.world, self.state, self.last_run, self.this_run) }
self.iter_inner()
}
}
@@ -2694,36 +2747,6 @@ impl<'w, 's, D: IterQueryData, F: QueryFilter> IntoIterator for &'w mut Query<'_
}
}
impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter> Query<'w, 's, D, F> {
/// Returns an [`Iterator`] over the query items, with the actual "inner" world lifetime.
///
/// This can only return immutable data (mutable data will be cast to an immutable form).
/// See [`Self::iter_mut`] for queries that contain at least one mutable component.
///
/// # Example
///
/// Here, the `report_names_system` iterates over the `Player` component of every entity
/// that contains it:
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(Component)]
/// # struct Player { name: String }
/// #
/// fn report_names_system(query: Query<&Player>) {
/// for player in &query {
/// println!("Say hello to {}!", player.name);
/// }
/// }
/// # bevy_ecs::system::assert_is_system(report_names_system);
/// ```
#[inline]
pub fn iter_inner(&self) -> QueryIter<'w, 's, D::ReadOnly, F> {
(*self).into_iter()
}
}
/// Type returned from [`Query::transmute_lens`] containing the new [`QueryState`].
///
/// Call [`query`](QueryLens::query) or [`into`](Into::into) to construct the resulting [`Query`]
@@ -12,13 +12,18 @@ and need additional trait bounds to ensure they are only used soundly.
An `IterQueryData` bound has been added to iteration methods on `Query`:
* `iter_mut`/ `iter_unsafe` / `into_iter`
* `into_iter`
* `iter_many_unique_mut` / `iter_many_unique_unsafe` / `iter_many_unique_inner`
* `get_many_mut` / `get_many_inner` / `get_many_unique_mut` / `get_many_unique_inner`
* `par_iter_mut` / `par_iter_inner` / `par_iter_many_unique_mut`
* `single_mut` / `single_inner`
* `iter_combinations_mut` / `iter_combinations_inner` / `iter_combinations_unsafe`
`iter`, `iter_many`, `par_iter`, and `single` have no extra bounds,
`iter_mut`, `iter_unsafe`, and `iter_inner` may be called with non-iterable data,
but the resulting `QueryIter` will not `impl Iterator`.
It may be iterated using streaming or lending iteration by calling the new `fetch_next` method.
`iter`, `iter_many`, `par_iter`, `single`, and `iter_combinations` have no extra bounds,
since read-only queries are always sound to iterate.
`iter_many_mut` and `iter_many_inner` methods have no extra bounds, either,
since they already prohibit concurrent access to multiple entities.
@@ -41,6 +46,11 @@ fn generic_func<D: QueryData>(query: Query<D>) {
fn generic_func<D: IterQueryData>(query: Query<D>) {
for item in &mut query { ... }
}
// 0.18, but with support for non-iterable query types
fn generic_func<D: QueryData>(mut query: Query<D>) {
let mut iter = query.iter_mut();
while let Some(item) = iter.fetch_next() { ... }
}
```
Conversely, manual implementations of `QueryData` may want to implement `IterQueryData` and `SingleEntityQueryData` if appropriate.