Skip to content

get unchecked_mut variants to by_id methods #5922

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 37 additions & 6 deletions crates/bevy_ecs/src/world/entity_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,32 @@ impl<'w> EntityRef<'w> {
)
}
}

/// Gets the component of the given [`ComponentId`] from the entity.
///
/// **You should prefer to use the typed API where possible and only
/// use this in cases where the actual component types are not known at
/// compile time.**
///
/// Unlike [`EntityRef::get`], this returns a raw pointer to the component,
/// instead of a reference.
///
/// # Safety
///
/// - The returned reference must never alias another reference to this component
/// - The returned reference must not be used after this component is moved which
/// may happen from **any** `insert_component`, `remove_component` or `despawn`
/// operation on this world (non-exhaustive list).
#[inline]
pub unsafe fn get_unchecked_mut_by_id(
&self,
component_id: ComponentId,
) -> Option<MutUntyped<'w>> {
self.world.components().get_info(component_id)?;
// SAFETY: entity_location is valid, component_id is valid as checked by the line above,
// the caller promises that they can uniquely access this component
get_mut_by_id(self.world, self.entity, self.location, component_id)
}
}

impl<'w> From<EntityMut<'w>> for EntityRef<'w> {
Expand Down Expand Up @@ -692,7 +718,7 @@ impl<'w> EntityMut<'w> {
#[inline]
pub fn get_mut_by_id(&mut self, component_id: ComponentId) -> Option<MutUntyped<'_>> {
self.world.components().get_info(component_id)?;
// SAFETY: entity_location is valid, component_id is valid as checked by the line above
// SAFETY: entity_location is valid, component_id is valid as checked by the line above, world access is unique
unsafe { get_mut_by_id(self.world, self.entity, self.location, component_id) }
}
}
Expand Down Expand Up @@ -1064,21 +1090,26 @@ pub(crate) unsafe fn get_mut<T: Component>(
})
}

// SAFETY: EntityLocation must be valid, component_id must be valid
// SAFETY:
// - EntityLocation must be valid, component_id must be valid
// - world access to the component must be valid, either because the caller has a `&mut` world or it synchronizes access like systems do
#[inline]
pub(crate) unsafe fn get_mut_by_id(
world: &mut World,
world: &World,
entity: Entity,
location: EntityLocation,
component_id: ComponentId,
) -> Option<MutUntyped> {
let change_tick = world.change_tick();
let info = world.components.get_info_unchecked(component_id);
// SAFETY: world access is unique, entity location and component_id required to be valid
// SAFETY: world access promised by the caller, entity location and component_id required to be valid
get_component_and_ticks(world, component_id, info.storage_type(), entity, location).map(
|(value, ticks)| MutUntyped {
value: value.assert_unique(),
ticks: Ticks::from_tick_cells(ticks, world.last_change_tick(), change_tick),
ticks: Ticks::from_tick_cells(
ticks,
world.last_change_tick(),
world.read_change_tick(),
),
},
)
}
Expand Down
36 changes: 28 additions & 8 deletions crates/bevy_ecs/src/world/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1499,23 +1499,43 @@ impl World {
/// use this in cases where the actual types are not known at compile time.**
#[inline]
pub fn get_resource_mut_by_id(&mut self, component_id: ComponentId) -> Option<MutUntyped<'_>> {
// SAFETY: unique world access
unsafe { self.get_resource_unchecked_mut_by_id(component_id) }
}

/// Gets a resource to the resource with the id [`ComponentId`] if it exists.
/// The returned pointer may be used to modify the resource, as long as the mutable borrow
/// of the [`World`] is still valid.
///
/// **You should prefer to use the typed API [`World::get_resource_mut`] where possible and only
/// use this in cases where the actual types are not known at compile time.**
///
/// # Safety
/// This will allow aliased mutable access to the given resource type. The caller must ensure
/// that there is either only one mutable access or multiple immutable accesses at a time.
#[inline]
pub unsafe fn get_resource_unchecked_mut_by_id(
&self,
component_id: ComponentId,
) -> Option<MutUntyped<'_>> {
let info = self.components.get_info(component_id)?;
if !info.is_send_and_sync() {
self.validate_non_send_access_untyped(info.name());
}

let change_tick = self.change_tick();

let (ptr, ticks) = self.get_resource_with_ticks(component_id)?;

// SAFETY: This function has exclusive access to the world so nothing aliases `ticks`.
// SAFETY:
// - index is in-bounds because the column is initialized and non-empty
// - no other reference to the ticks of the same row can exist at the same time
let ticks = unsafe { Ticks::from_tick_cells(ticks, self.last_change_tick(), change_tick) };
// - no other reference to the ticks, because the caller promises it
let ticks = unsafe {
Ticks::from_tick_cells(ticks, self.last_change_tick(), self.read_change_tick())
};

Some(MutUntyped {
// SAFETY: This function has exclusive access to the world so nothing aliases `ptr`.
value: unsafe { ptr.assert_unique() },
// SAFETY: the caller of this function has to ensure that they can mutably access this
// component for the duration of the `'_` lifetime
value: ptr.assert_unique(),
ticks,
})
}
Expand Down Expand Up @@ -1574,7 +1594,7 @@ impl World {
component_id: ComponentId,
) -> Option<MutUntyped<'_>> {
self.components().get_info(component_id)?;
// SAFETY: entity_location is valid, component_id is valid as checked by the line above
// SAFETY: entity_location is valid, component_id is valid as checked by the line above, world access is unique
unsafe {
get_mut_by_id(
self,
Expand Down