Skip to content

Commit 0fc6c66

Browse files
cBournhonesquealice-i-cecilechescock
authored
Allow querying multiple components from FilteredEntityMut (#21182)
# Objective #20265 introduced a way to fetch multiple mutable components from an `EntityMut`, but it's still impossible to do so via an `FilteredEntityMut`. I believe it is currently impossible to get two mutable components from a `FilteredEntityMut`, which somewhat limits use cases with dynamic queries. ## Solution A similar solution is harder to implement for `FilteredEntityMut` because the QueryData must go through the access checks, and it's not obvious to get the ComponentIds from a `ReleaseStateQueryData`. Instead, I opt in to provide a similar abstraction as `UnsafeEntityCell` and `UnsafeWorldCell`, which are both public and Clone: an opt-in escape catch for advanced users that can guarantee that they are not causing any aliasing violations. Here, instead we provide a method that copies the underlying `UnsafeEntityCell`, so the safety requirements are similar to `UnsafeEntityCell` and `UnsafeWorldCell`. ## Testing Added a doctest. --------- Co-authored-by: Alice Cecile <[email protected]> Co-authored-by: Chris Russell <[email protected]>
1 parent 9a4a119 commit 0fc6c66

File tree

2 files changed

+144
-4
lines changed

2 files changed

+144
-4
lines changed

crates/bevy_ecs/src/world/entity_ref.rs

Lines changed: 143 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3824,6 +3824,62 @@ impl ContainsEntity for FilteredEntityRef<'_, '_> {
38243824
// SAFETY: This type represents one Entity. We implement the comparison traits based on that Entity.
38253825
unsafe impl EntityEquivalent for FilteredEntityRef<'_, '_> {}
38263826

3827+
/// Variant of [`FilteredEntityMut`] that can be used to create copies of a [`FilteredEntityMut`], as long
3828+
/// as the user ensures that these won't cause aliasing violations.
3829+
///
3830+
/// This can be useful to mutably query multiple components from a single `FilteredEntityMut`.
3831+
///
3832+
/// ### Example Usage
3833+
///
3834+
/// ```
3835+
/// # use bevy_ecs::{prelude::*, world::{FilteredEntityMut, UnsafeFilteredEntityMut}};
3836+
/// #
3837+
/// # #[derive(Component)]
3838+
/// # struct A;
3839+
/// # #[derive(Component)]
3840+
/// # struct B;
3841+
/// #
3842+
/// # let mut world = World::new();
3843+
/// # world.spawn((A, B));
3844+
/// #
3845+
/// // This gives the `FilteredEntityMut` access to `&mut A` and `&mut B`.
3846+
/// let mut query = QueryBuilder::<FilteredEntityMut>::new(&mut world)
3847+
/// .data::<(&mut A, &mut B)>()
3848+
/// .build();
3849+
///
3850+
/// let mut filtered_entity: FilteredEntityMut = query.single_mut(&mut world).unwrap();
3851+
/// let unsafe_filtered_entity = UnsafeFilteredEntityMut::new_readonly(&filtered_entity);
3852+
/// // SAFETY: the original FilteredEntityMut accesses `&mut A` and the clone accesses `&mut B`, so no aliasing violations occur.
3853+
/// let mut filtered_entity_clone: FilteredEntityMut = unsafe { unsafe_filtered_entity.into_mut() };
3854+
/// let a: Mut<A> = filtered_entity.get_mut().unwrap();
3855+
/// let b: Mut<B> = filtered_entity_clone.get_mut().unwrap();
3856+
/// ```
3857+
#[derive(Copy, Clone)]
3858+
pub struct UnsafeFilteredEntityMut<'w, 's> {
3859+
entity: UnsafeEntityCell<'w>,
3860+
access: &'s Access,
3861+
}
3862+
3863+
impl<'w, 's> UnsafeFilteredEntityMut<'w, 's> {
3864+
/// Creates a [`UnsafeFilteredEntityMut`] that can be used to have multiple concurrent [`FilteredEntityMut`]s.
3865+
#[inline]
3866+
pub fn new_readonly(filtered_entity_mut: &FilteredEntityMut<'w, 's>) -> Self {
3867+
Self {
3868+
entity: filtered_entity_mut.entity,
3869+
access: filtered_entity_mut.access,
3870+
}
3871+
}
3872+
3873+
/// Returns a new instance of [`FilteredEntityMut`].
3874+
///
3875+
/// # Safety
3876+
/// - The user must ensure that no aliasing violations occur when using the returned `FilteredEntityMut`.
3877+
#[inline]
3878+
pub unsafe fn into_mut(self) -> FilteredEntityMut<'w, 's> {
3879+
FilteredEntityMut::new(self.entity, self.access)
3880+
}
3881+
}
3882+
38273883
/// Provides mutable access to a single entity and some of its components defined by the contained [`Access`].
38283884
///
38293885
/// To define the access when used as a [`QueryData`](crate::query::QueryData),
@@ -3847,6 +3903,8 @@ unsafe impl EntityEquivalent for FilteredEntityRef<'_, '_> {}
38473903
/// let mut filtered_entity: FilteredEntityMut = query.single_mut(&mut world).unwrap();
38483904
/// let component: Mut<A> = filtered_entity.get_mut().unwrap();
38493905
/// ```
3906+
///
3907+
/// Also see [`UnsafeFilteredEntityMut`] for a way to bypass borrow-checker restrictions.
38503908
pub struct FilteredEntityMut<'w, 's> {
38513909
entity: UnsafeEntityCell<'w>,
38523910
access: &'s Access,
@@ -3962,17 +4020,70 @@ impl<'w, 's> FilteredEntityMut<'w, 's> {
39624020
}
39634021

39644022
/// Gets mutable access to the component of type `T` for the current entity.
3965-
/// Returns `None` if the entity does not have a component of type `T`.
4023+
/// Returns `None` if the entity does not have a component of type `T` or if
4024+
/// the access does not include write access to `T`.
39664025
#[inline]
39674026
pub fn get_mut<T: Component<Mutability = Mutable>>(&mut self) -> Option<Mut<'_, T>> {
4027+
// SAFETY: we use a mutable reference to self, so we cannot use the `FilteredEntityMut` to access
4028+
// another component
4029+
unsafe { self.get_mut_unchecked() }
4030+
}
4031+
4032+
/// Gets mutable access to the component of type `T` for the current entity.
4033+
/// Returns `None` if the entity does not have a component of type `T` or if
4034+
/// the access does not include write access to `T`.
4035+
///
4036+
/// This only requires `&self`, and so may be used to get mutable access to multiple components.
4037+
///
4038+
/// # Example
4039+
///
4040+
/// ```
4041+
/// # use bevy_ecs::{prelude::*, world::FilteredEntityMut};
4042+
/// #
4043+
/// #[derive(Component)]
4044+
/// struct X(usize);
4045+
/// #[derive(Component)]
4046+
/// struct Y(usize);
4047+
///
4048+
/// # let mut world = World::default();
4049+
/// let mut entity = world.spawn((X(0), Y(0))).into_mutable();
4050+
///
4051+
/// // This gives the `FilteredEntityMut` access to `&mut X` and `&mut Y`.
4052+
/// let mut query = QueryBuilder::<FilteredEntityMut>::new(&mut world)
4053+
/// .data::<(&mut X, &mut Y)>()
4054+
/// .build();
4055+
///
4056+
/// let mut filtered_entity: FilteredEntityMut = query.single_mut(&mut world).unwrap();
4057+
///
4058+
/// // Get mutable access to two components at once
4059+
/// // SAFETY: We don't take any other references to `X` from this entity
4060+
/// let mut x = unsafe { filtered_entity.get_mut_unchecked::<X>() }.unwrap();
4061+
/// // SAFETY: We don't take any other references to `Y` from this entity
4062+
/// let mut y = unsafe { filtered_entity.get_mut_unchecked::<Y>() }.unwrap();
4063+
/// *x = X(1);
4064+
/// *y = Y(1);
4065+
/// ```
4066+
///
4067+
/// # Safety
4068+
///
4069+
/// No other references to the same component may exist at the same time as the returned reference.
4070+
///
4071+
/// # See also
4072+
///
4073+
/// - [`get_mut`](Self::get_mut) for the safe version.
4074+
#[inline]
4075+
pub unsafe fn get_mut_unchecked<T: Component<Mutability = Mutable>>(
4076+
&self,
4077+
) -> Option<Mut<'_, T>> {
39684078
let id = self
39694079
.entity
39704080
.world()
39714081
.components()
39724082
.get_valid_id(TypeId::of::<T>())?;
39734083
self.access
39744084
.has_component_write(id)
3975-
// SAFETY: We have write access
4085+
// SAFETY: We have permission to access the component mutable
4086+
// and we promise to not create other references to the same component
39764087
.then(|| unsafe { self.entity.get_mut() })
39774088
.flatten()
39784089
}
@@ -4052,9 +4163,38 @@ impl<'w, 's> FilteredEntityMut<'w, 's> {
40524163
/// which is only valid while the [`FilteredEntityMut`] is alive.
40534164
#[inline]
40544165
pub fn get_mut_by_id(&mut self, component_id: ComponentId) -> Option<MutUntyped<'_>> {
4166+
// SAFETY: we use a mutable reference to self, so we cannot use the `FilteredEntityMut` to access
4167+
// another component
4168+
unsafe { self.get_mut_by_id_unchecked(component_id) }
4169+
}
4170+
4171+
/// Gets a [`MutUntyped`] of the component of the given [`ComponentId`] from the entity.
4172+
///
4173+
/// **You should prefer to use the typed API [`Self::get_mut`] where possible and only
4174+
/// use this in cases where the actual component types are not known at
4175+
/// compile time.**
4176+
///
4177+
/// Unlike [`FilteredEntityMut::get_mut`], this returns a raw pointer to the component,
4178+
/// which is only valid while the [`FilteredEntityMut`] is alive.
4179+
///
4180+
/// This only requires `&self`, and so may be used to get mutable access to multiple components.
4181+
///
4182+
/// # Safety
4183+
///
4184+
/// No other references to the same component may exist at the same time as the returned reference.
4185+
///
4186+
/// # See also
4187+
///
4188+
/// - [`get_mut_by_id`](Self::get_mut_by_id) for the safe version.
4189+
#[inline]
4190+
pub unsafe fn get_mut_by_id_unchecked(
4191+
&self,
4192+
component_id: ComponentId,
4193+
) -> Option<MutUntyped<'_>> {
40554194
self.access
40564195
.has_component_write(component_id)
4057-
// SAFETY: We have write access
4196+
// SAFETY: We have permission to access the component mutable
4197+
// and we promise to not create other references to the same component
40584198
.then(|| unsafe { self.entity.get_mut_by_id(component_id).ok() })
40594199
.flatten()
40604200
}

crates/bevy_ecs/src/world/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub use entity_fetch::{EntityFetcher, WorldEntityFetch};
2323
pub use entity_ref::{
2424
ComponentEntry, DynamicComponentFetch, EntityMut, EntityMutExcept, EntityRef, EntityRefExcept,
2525
EntityWorldMut, FilteredEntityMut, FilteredEntityRef, OccupiedComponentEntry,
26-
TryFromFilteredError, VacantComponentEntry,
26+
TryFromFilteredError, UnsafeFilteredEntityMut, VacantComponentEntry,
2727
};
2828
pub use filtered_resource::*;
2929
pub use identifier::WorldId;

0 commit comments

Comments
 (0)