bevy/examples/ecs/custom_query_param.rs

180 lines
6.1 KiB
Rust
Raw Normal View History

//! This example illustrates the usage of the `WorldQuery` derive macro, which allows
//! defining custom query and filter types.
//!
//! While regular tuple queries work great in most of simple scenarios, using custom queries
//! declared as named structs can bring the following advantages:
//! - They help to avoid destructuring or using `q.0, q.1, ...` access pattern.
//! - Adding, removing components or changing items order with structs greatly reduces maintenance
//! burden, as you don't need to update statements that destructure tuples, care about order
//! of elements, etc. Instead, you can just add or remove places where a certain element is used.
//! - Named structs enable the composition pattern, that makes query types easier to re-use.
//! - You can bypass the limit of 15 components that exists for query tuples.
//!
//! For more details on the `WorldQuery` derive macro, see the trait documentation.
use bevy::{ecs::query::WorldQuery, prelude::*};
Use lifetimed, type erased pointers in bevy_ecs (#3001) # Objective `bevy_ecs` has large amounts of unsafe code which is hard to get right and makes it difficult to audit for soundness. ## Solution Introduce lifetimed, type-erased pointers: `Ptr<'a>` `PtrMut<'a>` `OwningPtr<'a>'` and `ThinSlicePtr<'a, T>` which are newtypes around a raw pointer with a lifetime and conceptually representing strong invariants about the pointee and validity of the pointer. The process of converting bevy_ecs to use these has already caught multiple cases of unsound behavior. ## Changelog TL;DR for release notes: `bevy_ecs` now uses lifetimed, type-erased pointers internally, significantly improving safety and legibility without sacrificing performance. This should have approximately no end user impact, unless you were meddling with the (unfortunately public) internals of `bevy_ecs`. - `Fetch`, `FilterFetch` and `ReadOnlyFetch` trait no longer have a `'state` lifetime - this was unneeded - `ReadOnly/Fetch` associated types on `WorldQuery` are now on a new `WorldQueryGats<'world>` trait - was required to work around lack of Generic Associated Types (we wish to express `type Fetch<'a>: Fetch<'a>`) - `derive(WorldQuery)` no longer requires `'w` lifetime on struct - this was unneeded, and improves the end user experience - `EntityMut::get_unchecked_mut` returns `&'_ mut T` not `&'w mut T` - allows easier use of unsafe API with less footguns, and can be worked around via lifetime transmutery as a user - `Bundle::from_components` now takes a `ctx` parameter to pass to the `FnMut` closure - required because closure return types can't borrow from captures - `Fetch::init` takes `&'world World`, `Fetch::set_archetype` takes `&'world Archetype` and `&'world Tables`, `Fetch::set_table` takes `&'world Table` - allows types implementing `Fetch` to store borrows into world - `WorldQuery` trait now has a `shrink` fn to shorten the lifetime in `Fetch::<'a>::Item` - this works around lack of subtyping of assoc types, rust doesnt allow you to turn `<T as Fetch<'static>>::Item'` into `<T as Fetch<'a>>::Item'` - `QueryCombinationsIter` requires this - Most types implementing `Fetch` now have a lifetime `'w` - allows the fetches to store borrows of world data instead of using raw pointers ## Migration guide - `EntityMut::get_unchecked_mut` returns a more restricted lifetime, there is no general way to migrate this as it depends on your code - `Bundle::from_components` implementations must pass the `ctx` arg to `func` - `Bundle::from_components` callers have to use a fn arg instead of closure captures for borrowing from world - Remove lifetime args on `derive(WorldQuery)` structs as it is nonsensical - `<Q as WorldQuery>::ReadOnly/Fetch` should be changed to either `RO/QueryFetch<'world>` or `<Q as WorldQueryGats<'world>>::ReadOnly/Fetch` - `<F as Fetch<'w, 's>>` should be changed to `<F as Fetch<'w>>` - Change the fn sigs of `Fetch::init/set_archetype/set_table` to match respective trait fn sigs - Implement the required `fn shrink` on any `WorldQuery` implementations - Move assoc types `Fetch` and `ReadOnlyFetch` on `WorldQuery` impls to `WorldQueryGats` impls - Pass an appropriate `'world` lifetime to whatever fetch struct you are for some reason using ### Type inference regression in some cases rustc may give spurrious errors when attempting to infer the `F` parameter on a query/querystate this can be fixed by manually specifying the type, i.e. `QueryState::new::<_, ()>(world)`. The error is rather confusing: ```rust= error[E0271]: type mismatch resolving `<() as Fetch<'_>>::Item == bool` --> crates/bevy_pbr/src/render/light.rs:1413:30 | 1413 | main_view_query: QueryState::new(world), | ^^^^^^^^^^^^^^^ expected `bool`, found `()` | = note: required because of the requirements on the impl of `for<'x> FilterFetch<'x>` for `<() as WorldQueryGats<'x>>::Fetch` note: required by a bound in `bevy_ecs::query::QueryState::<Q, F>::new` --> crates/bevy_ecs/src/query/state.rs:49:32 | 49 | for<'x> QueryFetch<'x, F>: FilterFetch<'x>, | ^^^^^^^^^^^^^^^ required by this bound in `bevy_ecs::query::QueryState::<Q, F>::new` ``` --- Made with help from @BoxyUwU and @alice-i-cecile Co-authored-by: Boxy <supbscripter@gmail.com>
2022-04-27 23:44:06 +00:00
use std::fmt::Debug;
Implement `WorldQuery` derive macro (#2713) # Objective - Closes #786 - Closes #2252 - Closes #2588 This PR implements a derive macro that allows users to define their queries as structs with named fields. ## Example ```rust #[derive(WorldQuery)] #[world_query(derive(Debug))] struct NumQuery<'w, T: Component, P: Component> { entity: Entity, u: UNumQuery<'w>, generic: GenericQuery<'w, T, P>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct UNumQuery<'w> { u_16: &'w u16, u_32_opt: Option<&'w u32>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct GenericQuery<'w, T: Component, P: Component> { generic: (&'w T, &'w P), } #[derive(WorldQuery)] #[world_query(filter)] struct NumQueryFilter<T: Component, P: Component> { _u_16: With<u16>, _u_32: With<u32>, _or: Or<(With<i16>, Changed<u16>, Added<u32>)>, _generic_tuple: (With<T>, With<P>), _without: Without<Option<u16>>, _tp: PhantomData<(T, P)>, } fn print_nums_readonly(query: Query<NumQuery<u64, i64>, NumQueryFilter<u64, i64>>) { for num in query.iter() { println!("{:#?}", num); } } #[derive(WorldQuery)] #[world_query(mutable, derive(Debug))] struct MutNumQuery<'w, T: Component, P: Component> { i_16: &'w mut i16, i_32_opt: Option<&'w mut i32>, } fn print_nums(mut query: Query<MutNumQuery, NumQueryFilter<u64, i64>>) { for num in query.iter_mut() { println!("{:#?}", num); } } ``` ## TODOs: - [x] Add support for `&T` and `&mut T` - [x] Test - [x] Add support for optional types - [x] Test - [x] Add support for `Entity` - [x] Test - [x] Add support for nested `WorldQuery` - [x] Test - [x] Add support for tuples - [x] Test - [x] Add support for generics - [x] Test - [x] Add support for query filters - [x] Test - [x] Add support for `PhantomData` - [x] Test - [x] Refactor `read_world_query_field_type_info` - [x] Properly document `readonly` attribute for nested queries and the static assertions that guarantee safety - [x] Test that we never implement `ReadOnlyFetch` for types that need mutable access - [x] Test that we insert static assertions for nested `WorldQuery` that a user marked as readonly
2022-02-24 00:19:49 +00:00
fn main() {
App::new()
.add_startup_system(spawn)
.add_system(print_components_read_only)
.add_system(print_components_iter_mut.after(print_components_read_only))
.add_system(print_components_iter.after(print_components_iter_mut))
.add_system(print_components_tuple.after(print_components_iter))
Implement `WorldQuery` derive macro (#2713) # Objective - Closes #786 - Closes #2252 - Closes #2588 This PR implements a derive macro that allows users to define their queries as structs with named fields. ## Example ```rust #[derive(WorldQuery)] #[world_query(derive(Debug))] struct NumQuery<'w, T: Component, P: Component> { entity: Entity, u: UNumQuery<'w>, generic: GenericQuery<'w, T, P>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct UNumQuery<'w> { u_16: &'w u16, u_32_opt: Option<&'w u32>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct GenericQuery<'w, T: Component, P: Component> { generic: (&'w T, &'w P), } #[derive(WorldQuery)] #[world_query(filter)] struct NumQueryFilter<T: Component, P: Component> { _u_16: With<u16>, _u_32: With<u32>, _or: Or<(With<i16>, Changed<u16>, Added<u32>)>, _generic_tuple: (With<T>, With<P>), _without: Without<Option<u16>>, _tp: PhantomData<(T, P)>, } fn print_nums_readonly(query: Query<NumQuery<u64, i64>, NumQueryFilter<u64, i64>>) { for num in query.iter() { println!("{:#?}", num); } } #[derive(WorldQuery)] #[world_query(mutable, derive(Debug))] struct MutNumQuery<'w, T: Component, P: Component> { i_16: &'w mut i16, i_32_opt: Option<&'w mut i32>, } fn print_nums(mut query: Query<MutNumQuery, NumQueryFilter<u64, i64>>) { for num in query.iter_mut() { println!("{:#?}", num); } } ``` ## TODOs: - [x] Add support for `&T` and `&mut T` - [x] Test - [x] Add support for optional types - [x] Test - [x] Add support for `Entity` - [x] Test - [x] Add support for nested `WorldQuery` - [x] Test - [x] Add support for tuples - [x] Test - [x] Add support for generics - [x] Test - [x] Add support for query filters - [x] Test - [x] Add support for `PhantomData` - [x] Test - [x] Refactor `read_world_query_field_type_info` - [x] Properly document `readonly` attribute for nested queries and the static assertions that guarantee safety - [x] Test that we never implement `ReadOnlyFetch` for types that need mutable access - [x] Test that we insert static assertions for nested `WorldQuery` that a user marked as readonly
2022-02-24 00:19:49 +00:00
.run();
}
#[derive(Component, Debug)]
struct ComponentA;
#[derive(Component, Debug)]
struct ComponentB;
#[derive(Component, Debug)]
struct ComponentC;
#[derive(Component, Debug)]
struct ComponentD;
#[derive(Component, Debug)]
struct ComponentZ;
#[derive(WorldQuery)]
#[world_query(derive(Debug))]
Use lifetimed, type erased pointers in bevy_ecs (#3001) # Objective `bevy_ecs` has large amounts of unsafe code which is hard to get right and makes it difficult to audit for soundness. ## Solution Introduce lifetimed, type-erased pointers: `Ptr<'a>` `PtrMut<'a>` `OwningPtr<'a>'` and `ThinSlicePtr<'a, T>` which are newtypes around a raw pointer with a lifetime and conceptually representing strong invariants about the pointee and validity of the pointer. The process of converting bevy_ecs to use these has already caught multiple cases of unsound behavior. ## Changelog TL;DR for release notes: `bevy_ecs` now uses lifetimed, type-erased pointers internally, significantly improving safety and legibility without sacrificing performance. This should have approximately no end user impact, unless you were meddling with the (unfortunately public) internals of `bevy_ecs`. - `Fetch`, `FilterFetch` and `ReadOnlyFetch` trait no longer have a `'state` lifetime - this was unneeded - `ReadOnly/Fetch` associated types on `WorldQuery` are now on a new `WorldQueryGats<'world>` trait - was required to work around lack of Generic Associated Types (we wish to express `type Fetch<'a>: Fetch<'a>`) - `derive(WorldQuery)` no longer requires `'w` lifetime on struct - this was unneeded, and improves the end user experience - `EntityMut::get_unchecked_mut` returns `&'_ mut T` not `&'w mut T` - allows easier use of unsafe API with less footguns, and can be worked around via lifetime transmutery as a user - `Bundle::from_components` now takes a `ctx` parameter to pass to the `FnMut` closure - required because closure return types can't borrow from captures - `Fetch::init` takes `&'world World`, `Fetch::set_archetype` takes `&'world Archetype` and `&'world Tables`, `Fetch::set_table` takes `&'world Table` - allows types implementing `Fetch` to store borrows into world - `WorldQuery` trait now has a `shrink` fn to shorten the lifetime in `Fetch::<'a>::Item` - this works around lack of subtyping of assoc types, rust doesnt allow you to turn `<T as Fetch<'static>>::Item'` into `<T as Fetch<'a>>::Item'` - `QueryCombinationsIter` requires this - Most types implementing `Fetch` now have a lifetime `'w` - allows the fetches to store borrows of world data instead of using raw pointers ## Migration guide - `EntityMut::get_unchecked_mut` returns a more restricted lifetime, there is no general way to migrate this as it depends on your code - `Bundle::from_components` implementations must pass the `ctx` arg to `func` - `Bundle::from_components` callers have to use a fn arg instead of closure captures for borrowing from world - Remove lifetime args on `derive(WorldQuery)` structs as it is nonsensical - `<Q as WorldQuery>::ReadOnly/Fetch` should be changed to either `RO/QueryFetch<'world>` or `<Q as WorldQueryGats<'world>>::ReadOnly/Fetch` - `<F as Fetch<'w, 's>>` should be changed to `<F as Fetch<'w>>` - Change the fn sigs of `Fetch::init/set_archetype/set_table` to match respective trait fn sigs - Implement the required `fn shrink` on any `WorldQuery` implementations - Move assoc types `Fetch` and `ReadOnlyFetch` on `WorldQuery` impls to `WorldQueryGats` impls - Pass an appropriate `'world` lifetime to whatever fetch struct you are for some reason using ### Type inference regression in some cases rustc may give spurrious errors when attempting to infer the `F` parameter on a query/querystate this can be fixed by manually specifying the type, i.e. `QueryState::new::<_, ()>(world)`. The error is rather confusing: ```rust= error[E0271]: type mismatch resolving `<() as Fetch<'_>>::Item == bool` --> crates/bevy_pbr/src/render/light.rs:1413:30 | 1413 | main_view_query: QueryState::new(world), | ^^^^^^^^^^^^^^^ expected `bool`, found `()` | = note: required because of the requirements on the impl of `for<'x> FilterFetch<'x>` for `<() as WorldQueryGats<'x>>::Fetch` note: required by a bound in `bevy_ecs::query::QueryState::<Q, F>::new` --> crates/bevy_ecs/src/query/state.rs:49:32 | 49 | for<'x> QueryFetch<'x, F>: FilterFetch<'x>, | ^^^^^^^^^^^^^^^ required by this bound in `bevy_ecs::query::QueryState::<Q, F>::new` ``` --- Made with help from @BoxyUwU and @alice-i-cecile Co-authored-by: Boxy <supbscripter@gmail.com>
2022-04-27 23:44:06 +00:00
struct ReadOnlyCustomQuery<T: Component + Debug, P: Component + Debug> {
Implement `WorldQuery` derive macro (#2713) # Objective - Closes #786 - Closes #2252 - Closes #2588 This PR implements a derive macro that allows users to define their queries as structs with named fields. ## Example ```rust #[derive(WorldQuery)] #[world_query(derive(Debug))] struct NumQuery<'w, T: Component, P: Component> { entity: Entity, u: UNumQuery<'w>, generic: GenericQuery<'w, T, P>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct UNumQuery<'w> { u_16: &'w u16, u_32_opt: Option<&'w u32>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct GenericQuery<'w, T: Component, P: Component> { generic: (&'w T, &'w P), } #[derive(WorldQuery)] #[world_query(filter)] struct NumQueryFilter<T: Component, P: Component> { _u_16: With<u16>, _u_32: With<u32>, _or: Or<(With<i16>, Changed<u16>, Added<u32>)>, _generic_tuple: (With<T>, With<P>), _without: Without<Option<u16>>, _tp: PhantomData<(T, P)>, } fn print_nums_readonly(query: Query<NumQuery<u64, i64>, NumQueryFilter<u64, i64>>) { for num in query.iter() { println!("{:#?}", num); } } #[derive(WorldQuery)] #[world_query(mutable, derive(Debug))] struct MutNumQuery<'w, T: Component, P: Component> { i_16: &'w mut i16, i_32_opt: Option<&'w mut i32>, } fn print_nums(mut query: Query<MutNumQuery, NumQueryFilter<u64, i64>>) { for num in query.iter_mut() { println!("{:#?}", num); } } ``` ## TODOs: - [x] Add support for `&T` and `&mut T` - [x] Test - [x] Add support for optional types - [x] Test - [x] Add support for `Entity` - [x] Test - [x] Add support for nested `WorldQuery` - [x] Test - [x] Add support for tuples - [x] Test - [x] Add support for generics - [x] Test - [x] Add support for query filters - [x] Test - [x] Add support for `PhantomData` - [x] Test - [x] Refactor `read_world_query_field_type_info` - [x] Properly document `readonly` attribute for nested queries and the static assertions that guarantee safety - [x] Test that we never implement `ReadOnlyFetch` for types that need mutable access - [x] Test that we insert static assertions for nested `WorldQuery` that a user marked as readonly
2022-02-24 00:19:49 +00:00
entity: Entity,
Use lifetimed, type erased pointers in bevy_ecs (#3001) # Objective `bevy_ecs` has large amounts of unsafe code which is hard to get right and makes it difficult to audit for soundness. ## Solution Introduce lifetimed, type-erased pointers: `Ptr<'a>` `PtrMut<'a>` `OwningPtr<'a>'` and `ThinSlicePtr<'a, T>` which are newtypes around a raw pointer with a lifetime and conceptually representing strong invariants about the pointee and validity of the pointer. The process of converting bevy_ecs to use these has already caught multiple cases of unsound behavior. ## Changelog TL;DR for release notes: `bevy_ecs` now uses lifetimed, type-erased pointers internally, significantly improving safety and legibility without sacrificing performance. This should have approximately no end user impact, unless you were meddling with the (unfortunately public) internals of `bevy_ecs`. - `Fetch`, `FilterFetch` and `ReadOnlyFetch` trait no longer have a `'state` lifetime - this was unneeded - `ReadOnly/Fetch` associated types on `WorldQuery` are now on a new `WorldQueryGats<'world>` trait - was required to work around lack of Generic Associated Types (we wish to express `type Fetch<'a>: Fetch<'a>`) - `derive(WorldQuery)` no longer requires `'w` lifetime on struct - this was unneeded, and improves the end user experience - `EntityMut::get_unchecked_mut` returns `&'_ mut T` not `&'w mut T` - allows easier use of unsafe API with less footguns, and can be worked around via lifetime transmutery as a user - `Bundle::from_components` now takes a `ctx` parameter to pass to the `FnMut` closure - required because closure return types can't borrow from captures - `Fetch::init` takes `&'world World`, `Fetch::set_archetype` takes `&'world Archetype` and `&'world Tables`, `Fetch::set_table` takes `&'world Table` - allows types implementing `Fetch` to store borrows into world - `WorldQuery` trait now has a `shrink` fn to shorten the lifetime in `Fetch::<'a>::Item` - this works around lack of subtyping of assoc types, rust doesnt allow you to turn `<T as Fetch<'static>>::Item'` into `<T as Fetch<'a>>::Item'` - `QueryCombinationsIter` requires this - Most types implementing `Fetch` now have a lifetime `'w` - allows the fetches to store borrows of world data instead of using raw pointers ## Migration guide - `EntityMut::get_unchecked_mut` returns a more restricted lifetime, there is no general way to migrate this as it depends on your code - `Bundle::from_components` implementations must pass the `ctx` arg to `func` - `Bundle::from_components` callers have to use a fn arg instead of closure captures for borrowing from world - Remove lifetime args on `derive(WorldQuery)` structs as it is nonsensical - `<Q as WorldQuery>::ReadOnly/Fetch` should be changed to either `RO/QueryFetch<'world>` or `<Q as WorldQueryGats<'world>>::ReadOnly/Fetch` - `<F as Fetch<'w, 's>>` should be changed to `<F as Fetch<'w>>` - Change the fn sigs of `Fetch::init/set_archetype/set_table` to match respective trait fn sigs - Implement the required `fn shrink` on any `WorldQuery` implementations - Move assoc types `Fetch` and `ReadOnlyFetch` on `WorldQuery` impls to `WorldQueryGats` impls - Pass an appropriate `'world` lifetime to whatever fetch struct you are for some reason using ### Type inference regression in some cases rustc may give spurrious errors when attempting to infer the `F` parameter on a query/querystate this can be fixed by manually specifying the type, i.e. `QueryState::new::<_, ()>(world)`. The error is rather confusing: ```rust= error[E0271]: type mismatch resolving `<() as Fetch<'_>>::Item == bool` --> crates/bevy_pbr/src/render/light.rs:1413:30 | 1413 | main_view_query: QueryState::new(world), | ^^^^^^^^^^^^^^^ expected `bool`, found `()` | = note: required because of the requirements on the impl of `for<'x> FilterFetch<'x>` for `<() as WorldQueryGats<'x>>::Fetch` note: required by a bound in `bevy_ecs::query::QueryState::<Q, F>::new` --> crates/bevy_ecs/src/query/state.rs:49:32 | 49 | for<'x> QueryFetch<'x, F>: FilterFetch<'x>, | ^^^^^^^^^^^^^^^ required by this bound in `bevy_ecs::query::QueryState::<Q, F>::new` ``` --- Made with help from @BoxyUwU and @alice-i-cecile Co-authored-by: Boxy <supbscripter@gmail.com>
2022-04-27 23:44:06 +00:00
a: &'static ComponentA,
b: Option<&'static ComponentB>,
nested: NestedQuery,
optional_nested: Option<NestedQuery>,
optional_tuple: Option<(&'static ComponentB, &'static ComponentZ)>,
generic: GenericQuery<T, P>,
empty: EmptyQuery,
Implement `WorldQuery` derive macro (#2713) # Objective - Closes #786 - Closes #2252 - Closes #2588 This PR implements a derive macro that allows users to define their queries as structs with named fields. ## Example ```rust #[derive(WorldQuery)] #[world_query(derive(Debug))] struct NumQuery<'w, T: Component, P: Component> { entity: Entity, u: UNumQuery<'w>, generic: GenericQuery<'w, T, P>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct UNumQuery<'w> { u_16: &'w u16, u_32_opt: Option<&'w u32>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct GenericQuery<'w, T: Component, P: Component> { generic: (&'w T, &'w P), } #[derive(WorldQuery)] #[world_query(filter)] struct NumQueryFilter<T: Component, P: Component> { _u_16: With<u16>, _u_32: With<u32>, _or: Or<(With<i16>, Changed<u16>, Added<u32>)>, _generic_tuple: (With<T>, With<P>), _without: Without<Option<u16>>, _tp: PhantomData<(T, P)>, } fn print_nums_readonly(query: Query<NumQuery<u64, i64>, NumQueryFilter<u64, i64>>) { for num in query.iter() { println!("{:#?}", num); } } #[derive(WorldQuery)] #[world_query(mutable, derive(Debug))] struct MutNumQuery<'w, T: Component, P: Component> { i_16: &'w mut i16, i_32_opt: Option<&'w mut i32>, } fn print_nums(mut query: Query<MutNumQuery, NumQueryFilter<u64, i64>>) { for num in query.iter_mut() { println!("{:#?}", num); } } ``` ## TODOs: - [x] Add support for `&T` and `&mut T` - [x] Test - [x] Add support for optional types - [x] Test - [x] Add support for `Entity` - [x] Test - [x] Add support for nested `WorldQuery` - [x] Test - [x] Add support for tuples - [x] Test - [x] Add support for generics - [x] Test - [x] Add support for query filters - [x] Test - [x] Add support for `PhantomData` - [x] Test - [x] Refactor `read_world_query_field_type_info` - [x] Properly document `readonly` attribute for nested queries and the static assertions that guarantee safety - [x] Test that we never implement `ReadOnlyFetch` for types that need mutable access - [x] Test that we insert static assertions for nested `WorldQuery` that a user marked as readonly
2022-02-24 00:19:49 +00:00
}
fn print_components_read_only(
query: Query<ReadOnlyCustomQuery<ComponentC, ComponentD>, QueryFilter<ComponentC, ComponentD>>,
) {
println!("Print components (read_only):");
for e in &query {
Implement `WorldQuery` derive macro (#2713) # Objective - Closes #786 - Closes #2252 - Closes #2588 This PR implements a derive macro that allows users to define their queries as structs with named fields. ## Example ```rust #[derive(WorldQuery)] #[world_query(derive(Debug))] struct NumQuery<'w, T: Component, P: Component> { entity: Entity, u: UNumQuery<'w>, generic: GenericQuery<'w, T, P>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct UNumQuery<'w> { u_16: &'w u16, u_32_opt: Option<&'w u32>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct GenericQuery<'w, T: Component, P: Component> { generic: (&'w T, &'w P), } #[derive(WorldQuery)] #[world_query(filter)] struct NumQueryFilter<T: Component, P: Component> { _u_16: With<u16>, _u_32: With<u32>, _or: Or<(With<i16>, Changed<u16>, Added<u32>)>, _generic_tuple: (With<T>, With<P>), _without: Without<Option<u16>>, _tp: PhantomData<(T, P)>, } fn print_nums_readonly(query: Query<NumQuery<u64, i64>, NumQueryFilter<u64, i64>>) { for num in query.iter() { println!("{:#?}", num); } } #[derive(WorldQuery)] #[world_query(mutable, derive(Debug))] struct MutNumQuery<'w, T: Component, P: Component> { i_16: &'w mut i16, i_32_opt: Option<&'w mut i32>, } fn print_nums(mut query: Query<MutNumQuery, NumQueryFilter<u64, i64>>) { for num in query.iter_mut() { println!("{:#?}", num); } } ``` ## TODOs: - [x] Add support for `&T` and `&mut T` - [x] Test - [x] Add support for optional types - [x] Test - [x] Add support for `Entity` - [x] Test - [x] Add support for nested `WorldQuery` - [x] Test - [x] Add support for tuples - [x] Test - [x] Add support for generics - [x] Test - [x] Add support for query filters - [x] Test - [x] Add support for `PhantomData` - [x] Test - [x] Refactor `read_world_query_field_type_info` - [x] Properly document `readonly` attribute for nested queries and the static assertions that guarantee safety - [x] Test that we never implement `ReadOnlyFetch` for types that need mutable access - [x] Test that we insert static assertions for nested `WorldQuery` that a user marked as readonly
2022-02-24 00:19:49 +00:00
println!("Entity: {:?}", e.entity);
println!("A: {:?}", e.a);
println!("B: {:?}", e.b);
println!("Nested: {:?}", e.nested);
println!("Optional nested: {:?}", e.optional_nested);
println!("Optional tuple: {:?}", e.optional_tuple);
println!("Generic: {:?}", e.generic);
}
println!();
}
// If you are going to mutate the data in a query, you must mark it with the `mutable` attribute.
// The `WorldQuery` derive macro will still create a read-only version, which will be have `ReadOnly`
// suffix.
// Note: if you want to use derive macros with read-only query variants, you need to pass them with
// using the `derive` attribute.
#[derive(WorldQuery)]
#[world_query(mutable, derive(Debug))]
Use lifetimed, type erased pointers in bevy_ecs (#3001) # Objective `bevy_ecs` has large amounts of unsafe code which is hard to get right and makes it difficult to audit for soundness. ## Solution Introduce lifetimed, type-erased pointers: `Ptr<'a>` `PtrMut<'a>` `OwningPtr<'a>'` and `ThinSlicePtr<'a, T>` which are newtypes around a raw pointer with a lifetime and conceptually representing strong invariants about the pointee and validity of the pointer. The process of converting bevy_ecs to use these has already caught multiple cases of unsound behavior. ## Changelog TL;DR for release notes: `bevy_ecs` now uses lifetimed, type-erased pointers internally, significantly improving safety and legibility without sacrificing performance. This should have approximately no end user impact, unless you were meddling with the (unfortunately public) internals of `bevy_ecs`. - `Fetch`, `FilterFetch` and `ReadOnlyFetch` trait no longer have a `'state` lifetime - this was unneeded - `ReadOnly/Fetch` associated types on `WorldQuery` are now on a new `WorldQueryGats<'world>` trait - was required to work around lack of Generic Associated Types (we wish to express `type Fetch<'a>: Fetch<'a>`) - `derive(WorldQuery)` no longer requires `'w` lifetime on struct - this was unneeded, and improves the end user experience - `EntityMut::get_unchecked_mut` returns `&'_ mut T` not `&'w mut T` - allows easier use of unsafe API with less footguns, and can be worked around via lifetime transmutery as a user - `Bundle::from_components` now takes a `ctx` parameter to pass to the `FnMut` closure - required because closure return types can't borrow from captures - `Fetch::init` takes `&'world World`, `Fetch::set_archetype` takes `&'world Archetype` and `&'world Tables`, `Fetch::set_table` takes `&'world Table` - allows types implementing `Fetch` to store borrows into world - `WorldQuery` trait now has a `shrink` fn to shorten the lifetime in `Fetch::<'a>::Item` - this works around lack of subtyping of assoc types, rust doesnt allow you to turn `<T as Fetch<'static>>::Item'` into `<T as Fetch<'a>>::Item'` - `QueryCombinationsIter` requires this - Most types implementing `Fetch` now have a lifetime `'w` - allows the fetches to store borrows of world data instead of using raw pointers ## Migration guide - `EntityMut::get_unchecked_mut` returns a more restricted lifetime, there is no general way to migrate this as it depends on your code - `Bundle::from_components` implementations must pass the `ctx` arg to `func` - `Bundle::from_components` callers have to use a fn arg instead of closure captures for borrowing from world - Remove lifetime args on `derive(WorldQuery)` structs as it is nonsensical - `<Q as WorldQuery>::ReadOnly/Fetch` should be changed to either `RO/QueryFetch<'world>` or `<Q as WorldQueryGats<'world>>::ReadOnly/Fetch` - `<F as Fetch<'w, 's>>` should be changed to `<F as Fetch<'w>>` - Change the fn sigs of `Fetch::init/set_archetype/set_table` to match respective trait fn sigs - Implement the required `fn shrink` on any `WorldQuery` implementations - Move assoc types `Fetch` and `ReadOnlyFetch` on `WorldQuery` impls to `WorldQueryGats` impls - Pass an appropriate `'world` lifetime to whatever fetch struct you are for some reason using ### Type inference regression in some cases rustc may give spurrious errors when attempting to infer the `F` parameter on a query/querystate this can be fixed by manually specifying the type, i.e. `QueryState::new::<_, ()>(world)`. The error is rather confusing: ```rust= error[E0271]: type mismatch resolving `<() as Fetch<'_>>::Item == bool` --> crates/bevy_pbr/src/render/light.rs:1413:30 | 1413 | main_view_query: QueryState::new(world), | ^^^^^^^^^^^^^^^ expected `bool`, found `()` | = note: required because of the requirements on the impl of `for<'x> FilterFetch<'x>` for `<() as WorldQueryGats<'x>>::Fetch` note: required by a bound in `bevy_ecs::query::QueryState::<Q, F>::new` --> crates/bevy_ecs/src/query/state.rs:49:32 | 49 | for<'x> QueryFetch<'x, F>: FilterFetch<'x>, | ^^^^^^^^^^^^^^^ required by this bound in `bevy_ecs::query::QueryState::<Q, F>::new` ``` --- Made with help from @BoxyUwU and @alice-i-cecile Co-authored-by: Boxy <supbscripter@gmail.com>
2022-04-27 23:44:06 +00:00
struct CustomQuery<T: Component + Debug, P: Component + Debug> {
Implement `WorldQuery` derive macro (#2713) # Objective - Closes #786 - Closes #2252 - Closes #2588 This PR implements a derive macro that allows users to define their queries as structs with named fields. ## Example ```rust #[derive(WorldQuery)] #[world_query(derive(Debug))] struct NumQuery<'w, T: Component, P: Component> { entity: Entity, u: UNumQuery<'w>, generic: GenericQuery<'w, T, P>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct UNumQuery<'w> { u_16: &'w u16, u_32_opt: Option<&'w u32>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct GenericQuery<'w, T: Component, P: Component> { generic: (&'w T, &'w P), } #[derive(WorldQuery)] #[world_query(filter)] struct NumQueryFilter<T: Component, P: Component> { _u_16: With<u16>, _u_32: With<u32>, _or: Or<(With<i16>, Changed<u16>, Added<u32>)>, _generic_tuple: (With<T>, With<P>), _without: Without<Option<u16>>, _tp: PhantomData<(T, P)>, } fn print_nums_readonly(query: Query<NumQuery<u64, i64>, NumQueryFilter<u64, i64>>) { for num in query.iter() { println!("{:#?}", num); } } #[derive(WorldQuery)] #[world_query(mutable, derive(Debug))] struct MutNumQuery<'w, T: Component, P: Component> { i_16: &'w mut i16, i_32_opt: Option<&'w mut i32>, } fn print_nums(mut query: Query<MutNumQuery, NumQueryFilter<u64, i64>>) { for num in query.iter_mut() { println!("{:#?}", num); } } ``` ## TODOs: - [x] Add support for `&T` and `&mut T` - [x] Test - [x] Add support for optional types - [x] Test - [x] Add support for `Entity` - [x] Test - [x] Add support for nested `WorldQuery` - [x] Test - [x] Add support for tuples - [x] Test - [x] Add support for generics - [x] Test - [x] Add support for query filters - [x] Test - [x] Add support for `PhantomData` - [x] Test - [x] Refactor `read_world_query_field_type_info` - [x] Properly document `readonly` attribute for nested queries and the static assertions that guarantee safety - [x] Test that we never implement `ReadOnlyFetch` for types that need mutable access - [x] Test that we insert static assertions for nested `WorldQuery` that a user marked as readonly
2022-02-24 00:19:49 +00:00
entity: Entity,
Use lifetimed, type erased pointers in bevy_ecs (#3001) # Objective `bevy_ecs` has large amounts of unsafe code which is hard to get right and makes it difficult to audit for soundness. ## Solution Introduce lifetimed, type-erased pointers: `Ptr<'a>` `PtrMut<'a>` `OwningPtr<'a>'` and `ThinSlicePtr<'a, T>` which are newtypes around a raw pointer with a lifetime and conceptually representing strong invariants about the pointee and validity of the pointer. The process of converting bevy_ecs to use these has already caught multiple cases of unsound behavior. ## Changelog TL;DR for release notes: `bevy_ecs` now uses lifetimed, type-erased pointers internally, significantly improving safety and legibility without sacrificing performance. This should have approximately no end user impact, unless you were meddling with the (unfortunately public) internals of `bevy_ecs`. - `Fetch`, `FilterFetch` and `ReadOnlyFetch` trait no longer have a `'state` lifetime - this was unneeded - `ReadOnly/Fetch` associated types on `WorldQuery` are now on a new `WorldQueryGats<'world>` trait - was required to work around lack of Generic Associated Types (we wish to express `type Fetch<'a>: Fetch<'a>`) - `derive(WorldQuery)` no longer requires `'w` lifetime on struct - this was unneeded, and improves the end user experience - `EntityMut::get_unchecked_mut` returns `&'_ mut T` not `&'w mut T` - allows easier use of unsafe API with less footguns, and can be worked around via lifetime transmutery as a user - `Bundle::from_components` now takes a `ctx` parameter to pass to the `FnMut` closure - required because closure return types can't borrow from captures - `Fetch::init` takes `&'world World`, `Fetch::set_archetype` takes `&'world Archetype` and `&'world Tables`, `Fetch::set_table` takes `&'world Table` - allows types implementing `Fetch` to store borrows into world - `WorldQuery` trait now has a `shrink` fn to shorten the lifetime in `Fetch::<'a>::Item` - this works around lack of subtyping of assoc types, rust doesnt allow you to turn `<T as Fetch<'static>>::Item'` into `<T as Fetch<'a>>::Item'` - `QueryCombinationsIter` requires this - Most types implementing `Fetch` now have a lifetime `'w` - allows the fetches to store borrows of world data instead of using raw pointers ## Migration guide - `EntityMut::get_unchecked_mut` returns a more restricted lifetime, there is no general way to migrate this as it depends on your code - `Bundle::from_components` implementations must pass the `ctx` arg to `func` - `Bundle::from_components` callers have to use a fn arg instead of closure captures for borrowing from world - Remove lifetime args on `derive(WorldQuery)` structs as it is nonsensical - `<Q as WorldQuery>::ReadOnly/Fetch` should be changed to either `RO/QueryFetch<'world>` or `<Q as WorldQueryGats<'world>>::ReadOnly/Fetch` - `<F as Fetch<'w, 's>>` should be changed to `<F as Fetch<'w>>` - Change the fn sigs of `Fetch::init/set_archetype/set_table` to match respective trait fn sigs - Implement the required `fn shrink` on any `WorldQuery` implementations - Move assoc types `Fetch` and `ReadOnlyFetch` on `WorldQuery` impls to `WorldQueryGats` impls - Pass an appropriate `'world` lifetime to whatever fetch struct you are for some reason using ### Type inference regression in some cases rustc may give spurrious errors when attempting to infer the `F` parameter on a query/querystate this can be fixed by manually specifying the type, i.e. `QueryState::new::<_, ()>(world)`. The error is rather confusing: ```rust= error[E0271]: type mismatch resolving `<() as Fetch<'_>>::Item == bool` --> crates/bevy_pbr/src/render/light.rs:1413:30 | 1413 | main_view_query: QueryState::new(world), | ^^^^^^^^^^^^^^^ expected `bool`, found `()` | = note: required because of the requirements on the impl of `for<'x> FilterFetch<'x>` for `<() as WorldQueryGats<'x>>::Fetch` note: required by a bound in `bevy_ecs::query::QueryState::<Q, F>::new` --> crates/bevy_ecs/src/query/state.rs:49:32 | 49 | for<'x> QueryFetch<'x, F>: FilterFetch<'x>, | ^^^^^^^^^^^^^^^ required by this bound in `bevy_ecs::query::QueryState::<Q, F>::new` ``` --- Made with help from @BoxyUwU and @alice-i-cecile Co-authored-by: Boxy <supbscripter@gmail.com>
2022-04-27 23:44:06 +00:00
a: &'static mut ComponentA,
b: Option<&'static mut ComponentB>,
nested: NestedQuery,
optional_nested: Option<NestedQuery>,
optional_tuple: Option<(NestedQuery, &'static mut ComponentZ)>,
generic: GenericQuery<T, P>,
empty: EmptyQuery,
Implement `WorldQuery` derive macro (#2713) # Objective - Closes #786 - Closes #2252 - Closes #2588 This PR implements a derive macro that allows users to define their queries as structs with named fields. ## Example ```rust #[derive(WorldQuery)] #[world_query(derive(Debug))] struct NumQuery<'w, T: Component, P: Component> { entity: Entity, u: UNumQuery<'w>, generic: GenericQuery<'w, T, P>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct UNumQuery<'w> { u_16: &'w u16, u_32_opt: Option<&'w u32>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct GenericQuery<'w, T: Component, P: Component> { generic: (&'w T, &'w P), } #[derive(WorldQuery)] #[world_query(filter)] struct NumQueryFilter<T: Component, P: Component> { _u_16: With<u16>, _u_32: With<u32>, _or: Or<(With<i16>, Changed<u16>, Added<u32>)>, _generic_tuple: (With<T>, With<P>), _without: Without<Option<u16>>, _tp: PhantomData<(T, P)>, } fn print_nums_readonly(query: Query<NumQuery<u64, i64>, NumQueryFilter<u64, i64>>) { for num in query.iter() { println!("{:#?}", num); } } #[derive(WorldQuery)] #[world_query(mutable, derive(Debug))] struct MutNumQuery<'w, T: Component, P: Component> { i_16: &'w mut i16, i_32_opt: Option<&'w mut i32>, } fn print_nums(mut query: Query<MutNumQuery, NumQueryFilter<u64, i64>>) { for num in query.iter_mut() { println!("{:#?}", num); } } ``` ## TODOs: - [x] Add support for `&T` and `&mut T` - [x] Test - [x] Add support for optional types - [x] Test - [x] Add support for `Entity` - [x] Test - [x] Add support for nested `WorldQuery` - [x] Test - [x] Add support for tuples - [x] Test - [x] Add support for generics - [x] Test - [x] Add support for query filters - [x] Test - [x] Add support for `PhantomData` - [x] Test - [x] Refactor `read_world_query_field_type_info` - [x] Properly document `readonly` attribute for nested queries and the static assertions that guarantee safety - [x] Test that we never implement `ReadOnlyFetch` for types that need mutable access - [x] Test that we insert static assertions for nested `WorldQuery` that a user marked as readonly
2022-02-24 00:19:49 +00:00
}
// This is a valid query as well, which would iterate over every entity.
#[derive(WorldQuery)]
#[world_query(derive(Debug))]
Use lifetimed, type erased pointers in bevy_ecs (#3001) # Objective `bevy_ecs` has large amounts of unsafe code which is hard to get right and makes it difficult to audit for soundness. ## Solution Introduce lifetimed, type-erased pointers: `Ptr<'a>` `PtrMut<'a>` `OwningPtr<'a>'` and `ThinSlicePtr<'a, T>` which are newtypes around a raw pointer with a lifetime and conceptually representing strong invariants about the pointee and validity of the pointer. The process of converting bevy_ecs to use these has already caught multiple cases of unsound behavior. ## Changelog TL;DR for release notes: `bevy_ecs` now uses lifetimed, type-erased pointers internally, significantly improving safety and legibility without sacrificing performance. This should have approximately no end user impact, unless you were meddling with the (unfortunately public) internals of `bevy_ecs`. - `Fetch`, `FilterFetch` and `ReadOnlyFetch` trait no longer have a `'state` lifetime - this was unneeded - `ReadOnly/Fetch` associated types on `WorldQuery` are now on a new `WorldQueryGats<'world>` trait - was required to work around lack of Generic Associated Types (we wish to express `type Fetch<'a>: Fetch<'a>`) - `derive(WorldQuery)` no longer requires `'w` lifetime on struct - this was unneeded, and improves the end user experience - `EntityMut::get_unchecked_mut` returns `&'_ mut T` not `&'w mut T` - allows easier use of unsafe API with less footguns, and can be worked around via lifetime transmutery as a user - `Bundle::from_components` now takes a `ctx` parameter to pass to the `FnMut` closure - required because closure return types can't borrow from captures - `Fetch::init` takes `&'world World`, `Fetch::set_archetype` takes `&'world Archetype` and `&'world Tables`, `Fetch::set_table` takes `&'world Table` - allows types implementing `Fetch` to store borrows into world - `WorldQuery` trait now has a `shrink` fn to shorten the lifetime in `Fetch::<'a>::Item` - this works around lack of subtyping of assoc types, rust doesnt allow you to turn `<T as Fetch<'static>>::Item'` into `<T as Fetch<'a>>::Item'` - `QueryCombinationsIter` requires this - Most types implementing `Fetch` now have a lifetime `'w` - allows the fetches to store borrows of world data instead of using raw pointers ## Migration guide - `EntityMut::get_unchecked_mut` returns a more restricted lifetime, there is no general way to migrate this as it depends on your code - `Bundle::from_components` implementations must pass the `ctx` arg to `func` - `Bundle::from_components` callers have to use a fn arg instead of closure captures for borrowing from world - Remove lifetime args on `derive(WorldQuery)` structs as it is nonsensical - `<Q as WorldQuery>::ReadOnly/Fetch` should be changed to either `RO/QueryFetch<'world>` or `<Q as WorldQueryGats<'world>>::ReadOnly/Fetch` - `<F as Fetch<'w, 's>>` should be changed to `<F as Fetch<'w>>` - Change the fn sigs of `Fetch::init/set_archetype/set_table` to match respective trait fn sigs - Implement the required `fn shrink` on any `WorldQuery` implementations - Move assoc types `Fetch` and `ReadOnlyFetch` on `WorldQuery` impls to `WorldQueryGats` impls - Pass an appropriate `'world` lifetime to whatever fetch struct you are for some reason using ### Type inference regression in some cases rustc may give spurrious errors when attempting to infer the `F` parameter on a query/querystate this can be fixed by manually specifying the type, i.e. `QueryState::new::<_, ()>(world)`. The error is rather confusing: ```rust= error[E0271]: type mismatch resolving `<() as Fetch<'_>>::Item == bool` --> crates/bevy_pbr/src/render/light.rs:1413:30 | 1413 | main_view_query: QueryState::new(world), | ^^^^^^^^^^^^^^^ expected `bool`, found `()` | = note: required because of the requirements on the impl of `for<'x> FilterFetch<'x>` for `<() as WorldQueryGats<'x>>::Fetch` note: required by a bound in `bevy_ecs::query::QueryState::<Q, F>::new` --> crates/bevy_ecs/src/query/state.rs:49:32 | 49 | for<'x> QueryFetch<'x, F>: FilterFetch<'x>, | ^^^^^^^^^^^^^^^ required by this bound in `bevy_ecs::query::QueryState::<Q, F>::new` ``` --- Made with help from @BoxyUwU and @alice-i-cecile Co-authored-by: Boxy <supbscripter@gmail.com>
2022-04-27 23:44:06 +00:00
struct EmptyQuery {
empty: (),
Implement `WorldQuery` derive macro (#2713) # Objective - Closes #786 - Closes #2252 - Closes #2588 This PR implements a derive macro that allows users to define their queries as structs with named fields. ## Example ```rust #[derive(WorldQuery)] #[world_query(derive(Debug))] struct NumQuery<'w, T: Component, P: Component> { entity: Entity, u: UNumQuery<'w>, generic: GenericQuery<'w, T, P>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct UNumQuery<'w> { u_16: &'w u16, u_32_opt: Option<&'w u32>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct GenericQuery<'w, T: Component, P: Component> { generic: (&'w T, &'w P), } #[derive(WorldQuery)] #[world_query(filter)] struct NumQueryFilter<T: Component, P: Component> { _u_16: With<u16>, _u_32: With<u32>, _or: Or<(With<i16>, Changed<u16>, Added<u32>)>, _generic_tuple: (With<T>, With<P>), _without: Without<Option<u16>>, _tp: PhantomData<(T, P)>, } fn print_nums_readonly(query: Query<NumQuery<u64, i64>, NumQueryFilter<u64, i64>>) { for num in query.iter() { println!("{:#?}", num); } } #[derive(WorldQuery)] #[world_query(mutable, derive(Debug))] struct MutNumQuery<'w, T: Component, P: Component> { i_16: &'w mut i16, i_32_opt: Option<&'w mut i32>, } fn print_nums(mut query: Query<MutNumQuery, NumQueryFilter<u64, i64>>) { for num in query.iter_mut() { println!("{:#?}", num); } } ``` ## TODOs: - [x] Add support for `&T` and `&mut T` - [x] Test - [x] Add support for optional types - [x] Test - [x] Add support for `Entity` - [x] Test - [x] Add support for nested `WorldQuery` - [x] Test - [x] Add support for tuples - [x] Test - [x] Add support for generics - [x] Test - [x] Add support for query filters - [x] Test - [x] Add support for `PhantomData` - [x] Test - [x] Refactor `read_world_query_field_type_info` - [x] Properly document `readonly` attribute for nested queries and the static assertions that guarantee safety - [x] Test that we never implement `ReadOnlyFetch` for types that need mutable access - [x] Test that we insert static assertions for nested `WorldQuery` that a user marked as readonly
2022-02-24 00:19:49 +00:00
}
#[derive(WorldQuery)]
#[world_query(derive(Debug))]
Use lifetimed, type erased pointers in bevy_ecs (#3001) # Objective `bevy_ecs` has large amounts of unsafe code which is hard to get right and makes it difficult to audit for soundness. ## Solution Introduce lifetimed, type-erased pointers: `Ptr<'a>` `PtrMut<'a>` `OwningPtr<'a>'` and `ThinSlicePtr<'a, T>` which are newtypes around a raw pointer with a lifetime and conceptually representing strong invariants about the pointee and validity of the pointer. The process of converting bevy_ecs to use these has already caught multiple cases of unsound behavior. ## Changelog TL;DR for release notes: `bevy_ecs` now uses lifetimed, type-erased pointers internally, significantly improving safety and legibility without sacrificing performance. This should have approximately no end user impact, unless you were meddling with the (unfortunately public) internals of `bevy_ecs`. - `Fetch`, `FilterFetch` and `ReadOnlyFetch` trait no longer have a `'state` lifetime - this was unneeded - `ReadOnly/Fetch` associated types on `WorldQuery` are now on a new `WorldQueryGats<'world>` trait - was required to work around lack of Generic Associated Types (we wish to express `type Fetch<'a>: Fetch<'a>`) - `derive(WorldQuery)` no longer requires `'w` lifetime on struct - this was unneeded, and improves the end user experience - `EntityMut::get_unchecked_mut` returns `&'_ mut T` not `&'w mut T` - allows easier use of unsafe API with less footguns, and can be worked around via lifetime transmutery as a user - `Bundle::from_components` now takes a `ctx` parameter to pass to the `FnMut` closure - required because closure return types can't borrow from captures - `Fetch::init` takes `&'world World`, `Fetch::set_archetype` takes `&'world Archetype` and `&'world Tables`, `Fetch::set_table` takes `&'world Table` - allows types implementing `Fetch` to store borrows into world - `WorldQuery` trait now has a `shrink` fn to shorten the lifetime in `Fetch::<'a>::Item` - this works around lack of subtyping of assoc types, rust doesnt allow you to turn `<T as Fetch<'static>>::Item'` into `<T as Fetch<'a>>::Item'` - `QueryCombinationsIter` requires this - Most types implementing `Fetch` now have a lifetime `'w` - allows the fetches to store borrows of world data instead of using raw pointers ## Migration guide - `EntityMut::get_unchecked_mut` returns a more restricted lifetime, there is no general way to migrate this as it depends on your code - `Bundle::from_components` implementations must pass the `ctx` arg to `func` - `Bundle::from_components` callers have to use a fn arg instead of closure captures for borrowing from world - Remove lifetime args on `derive(WorldQuery)` structs as it is nonsensical - `<Q as WorldQuery>::ReadOnly/Fetch` should be changed to either `RO/QueryFetch<'world>` or `<Q as WorldQueryGats<'world>>::ReadOnly/Fetch` - `<F as Fetch<'w, 's>>` should be changed to `<F as Fetch<'w>>` - Change the fn sigs of `Fetch::init/set_archetype/set_table` to match respective trait fn sigs - Implement the required `fn shrink` on any `WorldQuery` implementations - Move assoc types `Fetch` and `ReadOnlyFetch` on `WorldQuery` impls to `WorldQueryGats` impls - Pass an appropriate `'world` lifetime to whatever fetch struct you are for some reason using ### Type inference regression in some cases rustc may give spurrious errors when attempting to infer the `F` parameter on a query/querystate this can be fixed by manually specifying the type, i.e. `QueryState::new::<_, ()>(world)`. The error is rather confusing: ```rust= error[E0271]: type mismatch resolving `<() as Fetch<'_>>::Item == bool` --> crates/bevy_pbr/src/render/light.rs:1413:30 | 1413 | main_view_query: QueryState::new(world), | ^^^^^^^^^^^^^^^ expected `bool`, found `()` | = note: required because of the requirements on the impl of `for<'x> FilterFetch<'x>` for `<() as WorldQueryGats<'x>>::Fetch` note: required by a bound in `bevy_ecs::query::QueryState::<Q, F>::new` --> crates/bevy_ecs/src/query/state.rs:49:32 | 49 | for<'x> QueryFetch<'x, F>: FilterFetch<'x>, | ^^^^^^^^^^^^^^^ required by this bound in `bevy_ecs::query::QueryState::<Q, F>::new` ``` --- Made with help from @BoxyUwU and @alice-i-cecile Co-authored-by: Boxy <supbscripter@gmail.com>
2022-04-27 23:44:06 +00:00
struct NestedQuery {
c: &'static ComponentC,
d: Option<&'static ComponentD>,
Implement `WorldQuery` derive macro (#2713) # Objective - Closes #786 - Closes #2252 - Closes #2588 This PR implements a derive macro that allows users to define their queries as structs with named fields. ## Example ```rust #[derive(WorldQuery)] #[world_query(derive(Debug))] struct NumQuery<'w, T: Component, P: Component> { entity: Entity, u: UNumQuery<'w>, generic: GenericQuery<'w, T, P>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct UNumQuery<'w> { u_16: &'w u16, u_32_opt: Option<&'w u32>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct GenericQuery<'w, T: Component, P: Component> { generic: (&'w T, &'w P), } #[derive(WorldQuery)] #[world_query(filter)] struct NumQueryFilter<T: Component, P: Component> { _u_16: With<u16>, _u_32: With<u32>, _or: Or<(With<i16>, Changed<u16>, Added<u32>)>, _generic_tuple: (With<T>, With<P>), _without: Without<Option<u16>>, _tp: PhantomData<(T, P)>, } fn print_nums_readonly(query: Query<NumQuery<u64, i64>, NumQueryFilter<u64, i64>>) { for num in query.iter() { println!("{:#?}", num); } } #[derive(WorldQuery)] #[world_query(mutable, derive(Debug))] struct MutNumQuery<'w, T: Component, P: Component> { i_16: &'w mut i16, i_32_opt: Option<&'w mut i32>, } fn print_nums(mut query: Query<MutNumQuery, NumQueryFilter<u64, i64>>) { for num in query.iter_mut() { println!("{:#?}", num); } } ``` ## TODOs: - [x] Add support for `&T` and `&mut T` - [x] Test - [x] Add support for optional types - [x] Test - [x] Add support for `Entity` - [x] Test - [x] Add support for nested `WorldQuery` - [x] Test - [x] Add support for tuples - [x] Test - [x] Add support for generics - [x] Test - [x] Add support for query filters - [x] Test - [x] Add support for `PhantomData` - [x] Test - [x] Refactor `read_world_query_field_type_info` - [x] Properly document `readonly` attribute for nested queries and the static assertions that guarantee safety - [x] Test that we never implement `ReadOnlyFetch` for types that need mutable access - [x] Test that we insert static assertions for nested `WorldQuery` that a user marked as readonly
2022-02-24 00:19:49 +00:00
}
#[derive(WorldQuery)]
#[world_query(derive(Debug))]
Use lifetimed, type erased pointers in bevy_ecs (#3001) # Objective `bevy_ecs` has large amounts of unsafe code which is hard to get right and makes it difficult to audit for soundness. ## Solution Introduce lifetimed, type-erased pointers: `Ptr<'a>` `PtrMut<'a>` `OwningPtr<'a>'` and `ThinSlicePtr<'a, T>` which are newtypes around a raw pointer with a lifetime and conceptually representing strong invariants about the pointee and validity of the pointer. The process of converting bevy_ecs to use these has already caught multiple cases of unsound behavior. ## Changelog TL;DR for release notes: `bevy_ecs` now uses lifetimed, type-erased pointers internally, significantly improving safety and legibility without sacrificing performance. This should have approximately no end user impact, unless you were meddling with the (unfortunately public) internals of `bevy_ecs`. - `Fetch`, `FilterFetch` and `ReadOnlyFetch` trait no longer have a `'state` lifetime - this was unneeded - `ReadOnly/Fetch` associated types on `WorldQuery` are now on a new `WorldQueryGats<'world>` trait - was required to work around lack of Generic Associated Types (we wish to express `type Fetch<'a>: Fetch<'a>`) - `derive(WorldQuery)` no longer requires `'w` lifetime on struct - this was unneeded, and improves the end user experience - `EntityMut::get_unchecked_mut` returns `&'_ mut T` not `&'w mut T` - allows easier use of unsafe API with less footguns, and can be worked around via lifetime transmutery as a user - `Bundle::from_components` now takes a `ctx` parameter to pass to the `FnMut` closure - required because closure return types can't borrow from captures - `Fetch::init` takes `&'world World`, `Fetch::set_archetype` takes `&'world Archetype` and `&'world Tables`, `Fetch::set_table` takes `&'world Table` - allows types implementing `Fetch` to store borrows into world - `WorldQuery` trait now has a `shrink` fn to shorten the lifetime in `Fetch::<'a>::Item` - this works around lack of subtyping of assoc types, rust doesnt allow you to turn `<T as Fetch<'static>>::Item'` into `<T as Fetch<'a>>::Item'` - `QueryCombinationsIter` requires this - Most types implementing `Fetch` now have a lifetime `'w` - allows the fetches to store borrows of world data instead of using raw pointers ## Migration guide - `EntityMut::get_unchecked_mut` returns a more restricted lifetime, there is no general way to migrate this as it depends on your code - `Bundle::from_components` implementations must pass the `ctx` arg to `func` - `Bundle::from_components` callers have to use a fn arg instead of closure captures for borrowing from world - Remove lifetime args on `derive(WorldQuery)` structs as it is nonsensical - `<Q as WorldQuery>::ReadOnly/Fetch` should be changed to either `RO/QueryFetch<'world>` or `<Q as WorldQueryGats<'world>>::ReadOnly/Fetch` - `<F as Fetch<'w, 's>>` should be changed to `<F as Fetch<'w>>` - Change the fn sigs of `Fetch::init/set_archetype/set_table` to match respective trait fn sigs - Implement the required `fn shrink` on any `WorldQuery` implementations - Move assoc types `Fetch` and `ReadOnlyFetch` on `WorldQuery` impls to `WorldQueryGats` impls - Pass an appropriate `'world` lifetime to whatever fetch struct you are for some reason using ### Type inference regression in some cases rustc may give spurrious errors when attempting to infer the `F` parameter on a query/querystate this can be fixed by manually specifying the type, i.e. `QueryState::new::<_, ()>(world)`. The error is rather confusing: ```rust= error[E0271]: type mismatch resolving `<() as Fetch<'_>>::Item == bool` --> crates/bevy_pbr/src/render/light.rs:1413:30 | 1413 | main_view_query: QueryState::new(world), | ^^^^^^^^^^^^^^^ expected `bool`, found `()` | = note: required because of the requirements on the impl of `for<'x> FilterFetch<'x>` for `<() as WorldQueryGats<'x>>::Fetch` note: required by a bound in `bevy_ecs::query::QueryState::<Q, F>::new` --> crates/bevy_ecs/src/query/state.rs:49:32 | 49 | for<'x> QueryFetch<'x, F>: FilterFetch<'x>, | ^^^^^^^^^^^^^^^ required by this bound in `bevy_ecs::query::QueryState::<Q, F>::new` ``` --- Made with help from @BoxyUwU and @alice-i-cecile Co-authored-by: Boxy <supbscripter@gmail.com>
2022-04-27 23:44:06 +00:00
struct GenericQuery<T: Component, P: Component> {
generic: (&'static T, &'static P),
Implement `WorldQuery` derive macro (#2713) # Objective - Closes #786 - Closes #2252 - Closes #2588 This PR implements a derive macro that allows users to define their queries as structs with named fields. ## Example ```rust #[derive(WorldQuery)] #[world_query(derive(Debug))] struct NumQuery<'w, T: Component, P: Component> { entity: Entity, u: UNumQuery<'w>, generic: GenericQuery<'w, T, P>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct UNumQuery<'w> { u_16: &'w u16, u_32_opt: Option<&'w u32>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct GenericQuery<'w, T: Component, P: Component> { generic: (&'w T, &'w P), } #[derive(WorldQuery)] #[world_query(filter)] struct NumQueryFilter<T: Component, P: Component> { _u_16: With<u16>, _u_32: With<u32>, _or: Or<(With<i16>, Changed<u16>, Added<u32>)>, _generic_tuple: (With<T>, With<P>), _without: Without<Option<u16>>, _tp: PhantomData<(T, P)>, } fn print_nums_readonly(query: Query<NumQuery<u64, i64>, NumQueryFilter<u64, i64>>) { for num in query.iter() { println!("{:#?}", num); } } #[derive(WorldQuery)] #[world_query(mutable, derive(Debug))] struct MutNumQuery<'w, T: Component, P: Component> { i_16: &'w mut i16, i_32_opt: Option<&'w mut i32>, } fn print_nums(mut query: Query<MutNumQuery, NumQueryFilter<u64, i64>>) { for num in query.iter_mut() { println!("{:#?}", num); } } ``` ## TODOs: - [x] Add support for `&T` and `&mut T` - [x] Test - [x] Add support for optional types - [x] Test - [x] Add support for `Entity` - [x] Test - [x] Add support for nested `WorldQuery` - [x] Test - [x] Add support for tuples - [x] Test - [x] Add support for generics - [x] Test - [x] Add support for query filters - [x] Test - [x] Add support for `PhantomData` - [x] Test - [x] Refactor `read_world_query_field_type_info` - [x] Properly document `readonly` attribute for nested queries and the static assertions that guarantee safety - [x] Test that we never implement `ReadOnlyFetch` for types that need mutable access - [x] Test that we insert static assertions for nested `WorldQuery` that a user marked as readonly
2022-02-24 00:19:49 +00:00
}
#[derive(WorldQuery)]
struct QueryFilter<T: Component, P: Component> {
_c: With<ComponentC>,
_d: With<ComponentD>,
_or: Or<(Added<ComponentC>, Changed<ComponentD>, Without<ComponentZ>)>,
_generic_tuple: (With<T>, With<P>),
}
fn spawn(mut commands: Commands) {
Spawn now takes a Bundle (#6054) # Objective Now that we can consolidate Bundles and Components under a single insert (thanks to #2975 and #6039), almost 100% of world spawns now look like `world.spawn().insert((Some, Tuple, Here))`. Spawning an entity without any components is an extremely uncommon pattern, so it makes sense to give spawn the "first class" ergonomic api. This consolidated api should be made consistent across all spawn apis (such as World and Commands). ## Solution All `spawn` apis (`World::spawn`, `Commands:;spawn`, `ChildBuilder::spawn`, and `WorldChildBuilder::spawn`) now accept a bundle as input: ```rust // before: commands .spawn() .insert((A, B, C)); world .spawn() .insert((A, B, C); // after commands.spawn((A, B, C)); world.spawn((A, B, C)); ``` All existing instances of `spawn_bundle` have been deprecated in favor of the new `spawn` api. A new `spawn_empty` has been added, replacing the old `spawn` api. By allowing `world.spawn(some_bundle)` to replace `world.spawn().insert(some_bundle)`, this opened the door to removing the initial entity allocation in the "empty" archetype / table done in `spawn()` (and subsequent move to the actual archetype in `.insert(some_bundle)`). This improves spawn performance by over 10%: ![image](https://user-images.githubusercontent.com/2694663/191627587-4ab2f949-4ccd-4231-80eb-80dd4d9ad6b9.png) To take this measurement, I added a new `world_spawn` benchmark. Unfortunately, optimizing `Commands::spawn` is slightly less trivial, as Commands expose the Entity id of spawned entities prior to actually spawning. Doing the optimization would (naively) require assurances that the `spawn(some_bundle)` command is applied before all other commands involving the entity (which would not necessarily be true, if memory serves). Optimizing `Commands::spawn` this way does feel possible, but it will require careful thought (and maybe some additional checks), which deserves its own PR. For now, it has the same performance characteristics of the current `Commands::spawn_bundle` on main. **Note that 99% of this PR is simple renames and refactors. The only code that needs careful scrutiny is the new `World::spawn()` impl, which is relatively straightforward, but it has some new unsafe code (which re-uses battle tested BundlerSpawner code path).** --- ## Changelog - All `spawn` apis (`World::spawn`, `Commands:;spawn`, `ChildBuilder::spawn`, and `WorldChildBuilder::spawn`) now accept a bundle as input - All instances of `spawn_bundle` have been deprecated in favor of the new `spawn` api - World and Commands now have `spawn_empty()`, which is equivalent to the old `spawn()` behavior. ## Migration Guide ```rust // Old (0.8): commands .spawn() .insert_bundle((A, B, C)); // New (0.9) commands.spawn((A, B, C)); // Old (0.8): commands.spawn_bundle((A, B, C)); // New (0.9) commands.spawn((A, B, C)); // Old (0.8): let entity = commands.spawn().id(); // New (0.9) let entity = commands.spawn_empty().id(); // Old (0.8) let entity = world.spawn().id(); // New (0.9) let entity = world.spawn_empty(); ```
2022-09-23 19:55:54 +00:00
commands.spawn((ComponentA, ComponentB, ComponentC, ComponentD));
Implement `WorldQuery` derive macro (#2713) # Objective - Closes #786 - Closes #2252 - Closes #2588 This PR implements a derive macro that allows users to define their queries as structs with named fields. ## Example ```rust #[derive(WorldQuery)] #[world_query(derive(Debug))] struct NumQuery<'w, T: Component, P: Component> { entity: Entity, u: UNumQuery<'w>, generic: GenericQuery<'w, T, P>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct UNumQuery<'w> { u_16: &'w u16, u_32_opt: Option<&'w u32>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct GenericQuery<'w, T: Component, P: Component> { generic: (&'w T, &'w P), } #[derive(WorldQuery)] #[world_query(filter)] struct NumQueryFilter<T: Component, P: Component> { _u_16: With<u16>, _u_32: With<u32>, _or: Or<(With<i16>, Changed<u16>, Added<u32>)>, _generic_tuple: (With<T>, With<P>), _without: Without<Option<u16>>, _tp: PhantomData<(T, P)>, } fn print_nums_readonly(query: Query<NumQuery<u64, i64>, NumQueryFilter<u64, i64>>) { for num in query.iter() { println!("{:#?}", num); } } #[derive(WorldQuery)] #[world_query(mutable, derive(Debug))] struct MutNumQuery<'w, T: Component, P: Component> { i_16: &'w mut i16, i_32_opt: Option<&'w mut i32>, } fn print_nums(mut query: Query<MutNumQuery, NumQueryFilter<u64, i64>>) { for num in query.iter_mut() { println!("{:#?}", num); } } ``` ## TODOs: - [x] Add support for `&T` and `&mut T` - [x] Test - [x] Add support for optional types - [x] Test - [x] Add support for `Entity` - [x] Test - [x] Add support for nested `WorldQuery` - [x] Test - [x] Add support for tuples - [x] Test - [x] Add support for generics - [x] Test - [x] Add support for query filters - [x] Test - [x] Add support for `PhantomData` - [x] Test - [x] Refactor `read_world_query_field_type_info` - [x] Properly document `readonly` attribute for nested queries and the static assertions that guarantee safety - [x] Test that we never implement `ReadOnlyFetch` for types that need mutable access - [x] Test that we insert static assertions for nested `WorldQuery` that a user marked as readonly
2022-02-24 00:19:49 +00:00
}
fn print_components_iter_mut(
mut query: Query<CustomQuery<ComponentC, ComponentD>, QueryFilter<ComponentC, ComponentD>>,
) {
println!("Print components (iter_mut):");
for e in &mut query {
Implement `WorldQuery` derive macro (#2713) # Objective - Closes #786 - Closes #2252 - Closes #2588 This PR implements a derive macro that allows users to define their queries as structs with named fields. ## Example ```rust #[derive(WorldQuery)] #[world_query(derive(Debug))] struct NumQuery<'w, T: Component, P: Component> { entity: Entity, u: UNumQuery<'w>, generic: GenericQuery<'w, T, P>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct UNumQuery<'w> { u_16: &'w u16, u_32_opt: Option<&'w u32>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct GenericQuery<'w, T: Component, P: Component> { generic: (&'w T, &'w P), } #[derive(WorldQuery)] #[world_query(filter)] struct NumQueryFilter<T: Component, P: Component> { _u_16: With<u16>, _u_32: With<u32>, _or: Or<(With<i16>, Changed<u16>, Added<u32>)>, _generic_tuple: (With<T>, With<P>), _without: Without<Option<u16>>, _tp: PhantomData<(T, P)>, } fn print_nums_readonly(query: Query<NumQuery<u64, i64>, NumQueryFilter<u64, i64>>) { for num in query.iter() { println!("{:#?}", num); } } #[derive(WorldQuery)] #[world_query(mutable, derive(Debug))] struct MutNumQuery<'w, T: Component, P: Component> { i_16: &'w mut i16, i_32_opt: Option<&'w mut i32>, } fn print_nums(mut query: Query<MutNumQuery, NumQueryFilter<u64, i64>>) { for num in query.iter_mut() { println!("{:#?}", num); } } ``` ## TODOs: - [x] Add support for `&T` and `&mut T` - [x] Test - [x] Add support for optional types - [x] Test - [x] Add support for `Entity` - [x] Test - [x] Add support for nested `WorldQuery` - [x] Test - [x] Add support for tuples - [x] Test - [x] Add support for generics - [x] Test - [x] Add support for query filters - [x] Test - [x] Add support for `PhantomData` - [x] Test - [x] Refactor `read_world_query_field_type_info` - [x] Properly document `readonly` attribute for nested queries and the static assertions that guarantee safety - [x] Test that we never implement `ReadOnlyFetch` for types that need mutable access - [x] Test that we insert static assertions for nested `WorldQuery` that a user marked as readonly
2022-02-24 00:19:49 +00:00
// Re-declaring the variable to illustrate the type of the actual iterator item.
let e: CustomQueryItem<'_, _, _> = e;
println!("Entity: {:?}", e.entity);
println!("A: {:?}", e.a);
println!("B: {:?}", e.b);
println!("Optional nested: {:?}", e.optional_nested);
println!("Optional tuple: {:?}", e.optional_tuple);
println!("Nested: {:?}", e.nested);
println!("Generic: {:?}", e.generic);
}
println!();
}
fn print_components_iter(
query: Query<CustomQuery<ComponentC, ComponentD>, QueryFilter<ComponentC, ComponentD>>,
) {
println!("Print components (iter):");
for e in &query {
Implement `WorldQuery` derive macro (#2713) # Objective - Closes #786 - Closes #2252 - Closes #2588 This PR implements a derive macro that allows users to define their queries as structs with named fields. ## Example ```rust #[derive(WorldQuery)] #[world_query(derive(Debug))] struct NumQuery<'w, T: Component, P: Component> { entity: Entity, u: UNumQuery<'w>, generic: GenericQuery<'w, T, P>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct UNumQuery<'w> { u_16: &'w u16, u_32_opt: Option<&'w u32>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct GenericQuery<'w, T: Component, P: Component> { generic: (&'w T, &'w P), } #[derive(WorldQuery)] #[world_query(filter)] struct NumQueryFilter<T: Component, P: Component> { _u_16: With<u16>, _u_32: With<u32>, _or: Or<(With<i16>, Changed<u16>, Added<u32>)>, _generic_tuple: (With<T>, With<P>), _without: Without<Option<u16>>, _tp: PhantomData<(T, P)>, } fn print_nums_readonly(query: Query<NumQuery<u64, i64>, NumQueryFilter<u64, i64>>) { for num in query.iter() { println!("{:#?}", num); } } #[derive(WorldQuery)] #[world_query(mutable, derive(Debug))] struct MutNumQuery<'w, T: Component, P: Component> { i_16: &'w mut i16, i_32_opt: Option<&'w mut i32>, } fn print_nums(mut query: Query<MutNumQuery, NumQueryFilter<u64, i64>>) { for num in query.iter_mut() { println!("{:#?}", num); } } ``` ## TODOs: - [x] Add support for `&T` and `&mut T` - [x] Test - [x] Add support for optional types - [x] Test - [x] Add support for `Entity` - [x] Test - [x] Add support for nested `WorldQuery` - [x] Test - [x] Add support for tuples - [x] Test - [x] Add support for generics - [x] Test - [x] Add support for query filters - [x] Test - [x] Add support for `PhantomData` - [x] Test - [x] Refactor `read_world_query_field_type_info` - [x] Properly document `readonly` attribute for nested queries and the static assertions that guarantee safety - [x] Test that we never implement `ReadOnlyFetch` for types that need mutable access - [x] Test that we insert static assertions for nested `WorldQuery` that a user marked as readonly
2022-02-24 00:19:49 +00:00
// Re-declaring the variable to illustrate the type of the actual iterator item.
let e: CustomQueryReadOnlyItem<'_, _, _> = e;
println!("Entity: {:?}", e.entity);
println!("A: {:?}", e.a);
println!("B: {:?}", e.b);
println!("Nested: {:?}", e.nested);
println!("Generic: {:?}", e.generic);
}
println!();
}
type NestedTupleQuery<'w> = (&'w ComponentC, &'w ComponentD);
type GenericTupleQuery<'w, T, P> = (&'w T, &'w P);
fn print_components_tuple(
query: Query<
(
Entity,
&ComponentA,
&ComponentB,
NestedTupleQuery,
GenericTupleQuery<ComponentC, ComponentD>,
),
(
With<ComponentC>,
With<ComponentD>,
Or<(Added<ComponentC>, Changed<ComponentD>, Without<ComponentZ>)>,
),
>,
) {
println!("Print components (tuple):");
for (entity, a, b, nested, (generic_c, generic_d)) in &query {
Implement `WorldQuery` derive macro (#2713) # Objective - Closes #786 - Closes #2252 - Closes #2588 This PR implements a derive macro that allows users to define their queries as structs with named fields. ## Example ```rust #[derive(WorldQuery)] #[world_query(derive(Debug))] struct NumQuery<'w, T: Component, P: Component> { entity: Entity, u: UNumQuery<'w>, generic: GenericQuery<'w, T, P>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct UNumQuery<'w> { u_16: &'w u16, u_32_opt: Option<&'w u32>, } #[derive(WorldQuery)] #[world_query(derive(Debug))] struct GenericQuery<'w, T: Component, P: Component> { generic: (&'w T, &'w P), } #[derive(WorldQuery)] #[world_query(filter)] struct NumQueryFilter<T: Component, P: Component> { _u_16: With<u16>, _u_32: With<u32>, _or: Or<(With<i16>, Changed<u16>, Added<u32>)>, _generic_tuple: (With<T>, With<P>), _without: Without<Option<u16>>, _tp: PhantomData<(T, P)>, } fn print_nums_readonly(query: Query<NumQuery<u64, i64>, NumQueryFilter<u64, i64>>) { for num in query.iter() { println!("{:#?}", num); } } #[derive(WorldQuery)] #[world_query(mutable, derive(Debug))] struct MutNumQuery<'w, T: Component, P: Component> { i_16: &'w mut i16, i_32_opt: Option<&'w mut i32>, } fn print_nums(mut query: Query<MutNumQuery, NumQueryFilter<u64, i64>>) { for num in query.iter_mut() { println!("{:#?}", num); } } ``` ## TODOs: - [x] Add support for `&T` and `&mut T` - [x] Test - [x] Add support for optional types - [x] Test - [x] Add support for `Entity` - [x] Test - [x] Add support for nested `WorldQuery` - [x] Test - [x] Add support for tuples - [x] Test - [x] Add support for generics - [x] Test - [x] Add support for query filters - [x] Test - [x] Add support for `PhantomData` - [x] Test - [x] Refactor `read_world_query_field_type_info` - [x] Properly document `readonly` attribute for nested queries and the static assertions that guarantee safety - [x] Test that we never implement `ReadOnlyFetch` for types that need mutable access - [x] Test that we insert static assertions for nested `WorldQuery` that a user marked as readonly
2022-02-24 00:19:49 +00:00
println!("Entity: {:?}", entity);
println!("A: {:?}", a);
println!("B: {:?}", b);
println!("Nested: {:?} {:?}", nested.0, nested.1);
println!("Generic: {:?} {:?}", generic_c, generic_d);
}
}