Skip to content
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

Query::get_multiple and get_multiple_mut #3333

Closed
wants to merge 50 commits into from

Conversation

alice-i-cecile
Copy link
Member

@alice-i-cecile alice-i-cecile commented Dec 15, 2021

Objective

  • The inability to have multiple active mutable borrows into a query is a common source of borrow-checker pain for users.
  • This is a pointless restriction if and only if we can guarantee that the entities they are accessing are unique.
  • This could already by bypassed with get_unchecked, but that is an extremely unsafe API.
  • Closes Safe method for multiple mutable references to components from Query #2042.

Solution

  • Implement Query::get_multiple and get_multiple_mut, to provide access to the data of more than one entity at once.
  • get_multiple_mut enforces uniqueness (to ensure soundness), but get_multiple does not (to reduce overhead).
  • No equivalent methods were made on World, as multiple EntityMut cannot exist at once: the ability to add and remove components to entities with it make that design fundamentally unsound.
  • Even when working in an exclusive world context, users can still create a Query and then use these methods.
  • An iterator is returned, rather than a Vec or other collection in order to avoid allocations and ensure maximal flexibility.

Breaking

Technically, this is a breaking change, as I've added a AliasedMutability variant to the QueryEntityError enum.

@alice-i-cecile alice-i-cecile added A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use labels Dec 15, 2021
@github-actions github-actions bot added the S-Needs-Triage This issue needs to be labelled label Dec 15, 2021
@alice-i-cecile alice-i-cecile added S-Needs-Review and removed S-Needs-Triage This issue needs to be labelled labels Dec 15, 2021
crates/bevy_ecs/src/system/query.rs Outdated Show resolved Hide resolved
crates/bevy_ecs/src/system/query.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@nakedible nakedible left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Community review! Looks good to me, but strained myself to get at least something to comment 😉

crates/bevy_ecs/src/system/query.rs Outdated Show resolved Hide resolved
crates/bevy_ecs/src/system/query.rs Show resolved Hide resolved
@nakedible
Copy link
Contributor

Also, probably just stating the obvious here, but adding an error to QueryEntityError would not be a breaking change if it would be marked as #[non_exhaustive].

@alice-i-cecile
Copy link
Member Author

We have accepted that every release will be extremely breaking, at least for now ;) I'm reasonably happy with causing compiler errors to draw attention to new behavior in this case.

@yilinwei
Copy link
Contributor

For the lack of variadics, what about using and returning const generics? So [Entity; N] -> [&mut T; N]?

@alice-i-cecile alice-i-cecile added M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide and removed S-Needs-Migration-Guide labels Jan 20, 2022
alice-i-cecile and others added 9 commits February 14, 2022 15:37
Getting multiple EntityMut is fundamentally impossible due to the ability to add and remove components. Fixing this is out of scope for this PR.
Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
/// assert_eq!(*a_iterator.next().unwrap(), A(1));
/// ```
#[inline]
pub fn get_multiple<const N: usize>(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these should be implemented on QueryState with appropriate variants, then wired up to Query

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep... I don't love this whole API duplication, but I think that's a reasonable call.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is necessary if we want "self driving" queries with direct world access (and I think we do).

@alice-i-cecile alice-i-cecile removed the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label Feb 24, 2022
Copy link
Member

@BoxyUwU BoxyUwU left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should add a compile fail test making sure that you cant make a GetMultipleMut iterator and then call methods on Query and then use the results from GetMultipleMut to get an aliased mutable ref

crates/bevy_ecs/src/system/query.rs Outdated Show resolved Hide resolved
crates/bevy_ecs/src/system/query.rs Outdated Show resolved Hide resolved
crates/bevy_ecs/src/system/query.rs Outdated Show resolved Hide resolved
crates/bevy_ecs/src/system/query.rs Outdated Show resolved Hide resolved
@BoxyUwU
Copy link
Member

BoxyUwU commented Mar 21, 2022

I don't really think that we should be adding Query::from_state in this PR, its an entirely unrelated API addition. It should be possible to get a Query from World already:

let mut world = World::default();
let mut system_state = SystemState::<Query<&mut Foo>>::new(&mut world); 
let mut query = system_state.get_mut(&mut world);

alice-i-cecile and others added 3 commits March 21, 2022 12:45
Co-authored-by: Boxy <supbscripter@gmail.com>
Co-authored-by: Boxy <supbscripter@gmail.com>
Co-authored-by: Boxy <supbscripter@gmail.com>
@BoxyUwU
Copy link
Member

BoxyUwU commented Mar 21, 2022

Lol those errors are not super helpful... can be fixed by replacing impl Iterator with GetMultipleMut in fn get_multiple_mut and by doing Query::<'q, 's, Q, F>::get_unchecked in GetMultipleMut::next. changing Query::get from fn get(&'s self to fn get(&self seems to fix the error for fn get_multiple

Full credit to Boxy
@cart cart added this to the Bevy 0.7 milestone Mar 21, 2022
@alice-i-cecile alice-i-cecile marked this pull request as draft March 22, 2022 00:42
@alice-i-cecile
Copy link
Member Author

Closing in favor of the less messy #4298.

bors bot pushed a commit that referenced this pull request Mar 30, 2022
…4298)

# Objective

- The inability to have multiple active mutable borrows into a query is a common source of borrow-checker pain for users.
- This is a pointless restriction if and only if we can guarantee that the entities they are accessing are unique.
- This could already by bypassed with get_unchecked, but that is an extremely unsafe API.
- Closes #2042.

## Solution

- Add `get_multiple`, `get_multiple_mut` and their unchecked equivalents (`multiple` and `multiple_mut`) to `Query` and `QueryState`.
- Improve the `QueryEntityError` type to provide more useful error information.

## Changelog

- Added `get_multiple`, `get_multiple_mut` and their unchecked equivalents (`multiple` and `multiple_mut`) to Query and QueryState.

## Migration Guide

- The `QueryEntityError` enum now has a `AliasedMutability variant, and returns the offending entity.

## Context

This is a fresh attempt at #3333; rebasing was behaving very badly and it was important to rebase on top of the recent query soundness fixes. Many thanks to all the reviewers in that thread, especially @BoxyUwU for the help with lifetimes.

## To-do

- [x] Add compile fail tests
- [x] Successfully deduplicate code
- [x] Decide what to do about failing doc tests
- [x] Get some reviews for lifetime soundness
aevyrie pushed a commit to aevyrie/bevy that referenced this pull request Jun 7, 2022
…evyengine#4298)

# Objective

- The inability to have multiple active mutable borrows into a query is a common source of borrow-checker pain for users.
- This is a pointless restriction if and only if we can guarantee that the entities they are accessing are unique.
- This could already by bypassed with get_unchecked, but that is an extremely unsafe API.
- Closes bevyengine#2042.

## Solution

- Add `get_multiple`, `get_multiple_mut` and their unchecked equivalents (`multiple` and `multiple_mut`) to `Query` and `QueryState`.
- Improve the `QueryEntityError` type to provide more useful error information.

## Changelog

- Added `get_multiple`, `get_multiple_mut` and their unchecked equivalents (`multiple` and `multiple_mut`) to Query and QueryState.

## Migration Guide

- The `QueryEntityError` enum now has a `AliasedMutability variant, and returns the offending entity.

## Context

This is a fresh attempt at bevyengine#3333; rebasing was behaving very badly and it was important to rebase on top of the recent query soundness fixes. Many thanks to all the reviewers in that thread, especially @BoxyUwU for the help with lifetimes.

## To-do

- [x] Add compile fail tests
- [x] Successfully deduplicate code
- [x] Decide what to do about failing doc tests
- [x] Get some reviews for lifetime soundness
ItsDoot pushed a commit to ItsDoot/bevy that referenced this pull request Feb 1, 2023
…evyengine#4298)

# Objective

- The inability to have multiple active mutable borrows into a query is a common source of borrow-checker pain for users.
- This is a pointless restriction if and only if we can guarantee that the entities they are accessing are unique.
- This could already by bypassed with get_unchecked, but that is an extremely unsafe API.
- Closes bevyengine#2042.

## Solution

- Add `get_multiple`, `get_multiple_mut` and their unchecked equivalents (`multiple` and `multiple_mut`) to `Query` and `QueryState`.
- Improve the `QueryEntityError` type to provide more useful error information.

## Changelog

- Added `get_multiple`, `get_multiple_mut` and their unchecked equivalents (`multiple` and `multiple_mut`) to Query and QueryState.

## Migration Guide

- The `QueryEntityError` enum now has a `AliasedMutability variant, and returns the offending entity.

## Context

This is a fresh attempt at bevyengine#3333; rebasing was behaving very badly and it was important to rebase on top of the recent query soundness fixes. Many thanks to all the reviewers in that thread, especially @BoxyUwU for the help with lifetimes.

## To-do

- [x] Add compile fail tests
- [x] Successfully deduplicate code
- [x] Decide what to do about failing doc tests
- [x] Get some reviews for lifetime soundness
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-Usability A targeted quality-of-life change that makes Bevy easier to use M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Safe method for multiple mutable references to components from Query
8 participants