Allow the creation of Query directly from the World
- Dominant language
- Rust
- Stars
- 48.2k
- Forks
- 4.8k
- Avg merge
- 3d 22h
- Merged PRs (30d)
- 161
Description
## What problem does this solve or what need does it fill?
Querying the world directly always the creation of `QueryState`, rather than returning a `Query` that can be worked with directly.
```rust
let query_state = world.query::<(&Foo, &mut Bar), With>();
for (foo, bar) in query_state.iter(&world){
assert!(foo > bar);
}
```
This is directly relevant to:
1. Exclusive systems. We could cache this state in a `Local`, but this is rarely done in practice.
2. Commands. Under the current commands model, there is nowhere to cache this state.
3. Integration tests. There is effectively no point caching this state, both because the queries are often not repeated, and because we are not in a performance-constrained setting.
The existing approach is confusing to beginners, heavy on boilerplate and directly exposes end users to `QueryState`, which should largely be engine-internal.
## What solution would you like?
Create two core methods on `World`, replacing the current `World::query` and `World::query_filtered`:
1. `World::query`: returns a stateless Query directly from the world. This is used in commands and integration tests.
2. `World::query_state`: returns a `QueryState`. This is used in engine internals, and exclusive systems.
In order to get this to work we need to:
1. Allow `Query` to store a `&QueryState` or a new `InternalQueryState` value, rather than just a `QueryState`.
2. Tweak the initialization methods.
3. Warn when `Added` or `Changed` are used with a `InternalQueryState`.
The example above becomes the much more direct and familiar:
```rust
for (foo, bar) in world.query::<(&Foo, &mut Bar), With>().iter(){
assert!(foo > bar);
}
```
## What alternative(s) have you considered?
1. Use and store a `QueryState` when writing integration tests and custom commands. Very boilerplate heavy and confusing to new users.
2. Write and use one-shot systems (#2192) for integration testing. Rather boilerplate-heavy, has some serious open questions and virtually all of the same issues around statelessness.
3. Write helper methods (in the engine, or in end user test code) that wrap this boilerplate. This doesn't save much work, and seriously reduces clarity and directness, especially as the methods proliferate. This was the approach taken in #3839.
## Additional context
Closely related to #3774.
Contributor guide
Assessment
This issue has not been assessed yet.