Skip to content

Store QueryState in Query as a Cow and remove distinct QueryLens type #15848

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

Closed
wants to merge 6 commits into from

Conversation

ItsDoot
Copy link
Contributor

@ItsDoot ItsDoot commented Oct 11, 2024

Objective

The QueryLens type is basically a Query, except it stores the QueryState itself rather than a reference to it. We can unify Query and QueryLens by allowing Query to hold either a reference or a value, which Cow provides. As a byproduct, we can also now also return Querys from World::query, rather than QueryStates.

Solution

The following changes have been made:

  • QueryState can now be Cloned (required by Cow)
    • All WorldQuery::State types are now required to be Clone.
  • Functions on Query returning 's have been replaced with '_.
  • The current World::query/World::query_filtered functions have been renamed to World::query_state/World::query_state_filtered.
  • New World::query/World::query_filtered functions have been added that return Query instead of QueryState.
  • New type conversions have been added:
    • Query<D, F>::into_state(self) -> QueryState<D, F> (clones the state if it was borrowed)
    • QueryState<D, F>::as_query(&mut self, &mut World) -> Query<D, F>
    • QueryState<D, F>::as_readonly_query(&self, &World) -> Query<D::ReadOnly, F>
    • QueryState<D, F>::into_query(self, &mut World) -> Query<D, F>
    • QueryState<D, F>::into_readonly_query(self, &World) -> Query<D::ReadOnly, F>
    • QueryState<D, F>::into_readonly(self) -> QueryState<D::ReadOnly, F>
      • Similar to QueryState::as_readonly, but by value instead of by reference.
  • The QueryLens type has been removed. Query can function exactly like it.
    • Query::transmute_lens_filtered(&mut self) -> QueryLens changed to Query::transmute_filtered(&mut self) -> Query
    • Query::transmute_lens(&mut self) -> QueryLens changed to Query::transmute(&mut self) -> Query
    • Query::join(&mut self, other: &mut Query) -> QueryLens changed to Query::join(&mut self, other: &mut Query) -> Query
    • Query::join_filtered(&mut self, other: &mut Query) -> QueryLens changed to Query::join_filtered(&mut self, other: &mut Query) -> Query
  • The state structs for QueryData/QueryFilter derives now impl Clone.

Testing

  • Current tests pass.
  • TODO: change compile fail test for query lens.

Showcase

TODO?

Migration Guide

  • QueryLens usages should be replaced with Query.
    • Query::transmute_lens_filtered(&mut self) -> QueryLens changed to Query::transmute_filtered(&mut self) -> Query
    • Query::transmute_lens(&mut self) -> QueryLens changed to Query::transmute(&mut self) -> Query
    • Query::join(&mut self, other: &mut Query) -> QueryLens changed to Query::join(&mut self, other: &mut Query) -> Query
    • Query::join_filtered(&mut self, other: &mut Query) -> QueryLens changed to Query::join_filtered(&mut self, other: &mut Query) -> Query
  • All WorldQuery::State types are now required to implement Clone.
  • The old World::query and World::query_filtered functions were renamed to World::query_state and World::query_state_filtered, respectively.
  • Query functions returning data with a 's now return '_, i.e. the query state lifetime is now bound by the Query instance rather than the contained QueryState.

@ItsDoot ItsDoot added A-ECS Entities, components, systems, and events C-Code-Quality A section of code that is hard to understand or change C-Usability A targeted quality-of-life change that makes Bevy easier to use D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes D-Unsafe Touches with unsafe code in some way labels Oct 11, 2024
@ItsDoot ItsDoot added the S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged label Oct 11, 2024
@alice-i-cecile alice-i-cecile added this to the 0.16 milestone Oct 12, 2024
@Trashtalk217 Trashtalk217 self-assigned this Oct 16, 2024
@hymm
Copy link
Contributor

hymm commented Oct 28, 2024

The old World::query and World::query_filtered functions were renamed to World::query_state and World::query_state_filtered, respectively.

It's probably worth moving this change to a separate pr. Makes this pr a bit hard to review.

@ItsDoot
Copy link
Contributor Author

ItsDoot commented Jan 1, 2025

Going to take a different approach to merging Query and QueryLens instead after I finish up some other stuff, if nobody else gets to it first.

@ItsDoot ItsDoot closed this Jan 1, 2025
github-merge-queue bot pushed a commit that referenced this pull request Feb 5, 2025
# Objective

Simplify and expand the API for `QueryState`.  

`QueryState` has a lot of methods that mirror those on `Query`. These
are then multiplied by variants that take `&World`, `&mut World`, and
`UnsafeWorldCell`. In addition, many of them have `_manual` variants
that take `&QueryState` and avoid calling `update_archetypes()`. Not all
of the combinations exist, however, so some operations are not possible.

## Solution

Introduce methods to get a `Query` from a `QueryState`. That will reduce
duplication between the types, and ensure that the full `Query` API is
always available for `QueryState`.

Introduce methods on `Query` that consume the query to return types with
the full `'w` lifetime. This avoids issues with borrowing where things
like `query_state.query(&world).get(entity)` don't work because they
borrow from the temporary `Query`.

Finally, implement `Copy` for read-only `Query`s. `get_inner` and
`iter_inner` currently take `&self`, so changing them to consume `self`
would be a breaking change. By making `Query: Copy`, they can consume a
copy of `self` and continue to work.

The consuming methods also let us simplify the implementation of methods
on `Query`, by doing `fn foo(&self) { self.as_readonly().foo_inner() }`
and `fn foo_mut(&mut self) { self.reborrow().foo_inner() }`. That
structure makes it more difficult to accidentally extend lifetimes,
since the safe `as_readonly()` and `reborrow()` methods shrink them
appropriately. The optimizer is able to see that they are both identity
functions and inline them, so there should be no performance cost.

Note that this change would conflict with #15848. If `QueryState` is
stored as a `Cow`, then the consuming methods cannot be implemented, and
`Copy` cannot be implemented.

## Future Work

The next step is to mark the methods on `QueryState` as `#[deprecated]`,
and move the implementations into `Query`.

## Migration Guide

`Query::to_readonly` has been renamed to `Query::as_readonly`.
mrchantey pushed a commit to mrchantey/bevy that referenced this pull request Feb 17, 2025
# Objective

Simplify and expand the API for `QueryState`.  

`QueryState` has a lot of methods that mirror those on `Query`. These
are then multiplied by variants that take `&World`, `&mut World`, and
`UnsafeWorldCell`. In addition, many of them have `_manual` variants
that take `&QueryState` and avoid calling `update_archetypes()`. Not all
of the combinations exist, however, so some operations are not possible.

## Solution

Introduce methods to get a `Query` from a `QueryState`. That will reduce
duplication between the types, and ensure that the full `Query` API is
always available for `QueryState`.

Introduce methods on `Query` that consume the query to return types with
the full `'w` lifetime. This avoids issues with borrowing where things
like `query_state.query(&world).get(entity)` don't work because they
borrow from the temporary `Query`.

Finally, implement `Copy` for read-only `Query`s. `get_inner` and
`iter_inner` currently take `&self`, so changing them to consume `self`
would be a breaking change. By making `Query: Copy`, they can consume a
copy of `self` and continue to work.

The consuming methods also let us simplify the implementation of methods
on `Query`, by doing `fn foo(&self) { self.as_readonly().foo_inner() }`
and `fn foo_mut(&mut self) { self.reborrow().foo_inner() }`. That
structure makes it more difficult to accidentally extend lifetimes,
since the safe `as_readonly()` and `reborrow()` methods shrink them
appropriately. The optimizer is able to see that they are both identity
functions and inline them, so there should be no performance cost.

Note that this change would conflict with bevyengine#15848. If `QueryState` is
stored as a `Cow`, then the consuming methods cannot be implemented, and
`Copy` cannot be implemented.

## Future Work

The next step is to mark the methods on `QueryState` as `#[deprecated]`,
and move the implementations into `Query`.

## Migration Guide

`Query::to_readonly` has been renamed to `Query::as_readonly`.
@chescock
Copy link
Contributor

chescock commented Mar 5, 2025

I made an attempt at doing this with type parameters: #18162

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events C-Code-Quality A section of code that is hard to understand or change C-Usability A targeted quality-of-life change that makes Bevy easier to use D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes D-Unsafe Touches with unsafe code in some way S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants