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

[Merged by Bors] - Make EntityRef::new unsafe #7222

Closed
16 changes: 13 additions & 3 deletions crates/bevy_ecs/src/world/entity_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,13 @@ pub struct EntityRef<'w> {
}

impl<'w> EntityRef<'w> {
/// # Safety
///
/// Entity and location _must_ be valid for the provided world.
#[inline]
pub(crate) fn new(world: &'w World, entity: Entity, location: EntityLocation) -> Self {
pub(crate) unsafe fn new(world: &'w World, entity: Entity, location: EntityLocation) -> Self {
debug_assert!(world.entities().contains(entity));

Self {
world,
entity,
Expand Down Expand Up @@ -193,7 +198,9 @@ impl<'w> EntityRef<'w> {

impl<'w> From<EntityMut<'w>> for EntityRef<'w> {
fn from(entity_mut: EntityMut<'w>) -> EntityRef<'w> {
EntityRef::new(entity_mut.world, entity_mut.entity, entity_mut.location)
// SAFETY: the safety invariants on EntityMut and EntityRef are identical
// and EntityMut is promised to be valid by construction.
unsafe { EntityRef::new(entity_mut.world, entity_mut.entity, entity_mut.location) }
}
}

Expand All @@ -206,13 +213,16 @@ pub struct EntityMut<'w> {

impl<'w> EntityMut<'w> {
/// # Safety
/// entity and location _must_ be valid
///
/// Entity and location _must_ be valid for the provided world.
#[inline]
pub(crate) unsafe fn new(
world: &'w mut World,
entity: Entity,
location: EntityLocation,
) -> Self {
debug_assert!(world.entities().contains(entity));

EntityMut {
world,
entity,
Expand Down
10 changes: 8 additions & 2 deletions crates/bevy_ecs/src/world/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,10 @@ impl World {
#[inline]
pub fn get_entity(&self, entity: Entity) -> Option<EntityRef> {
let location = self.entities.get(entity)?;
Some(EntityRef::new(self, entity, location))
// SAFETY: if the Entity is invalid, the function returns early.
// Additionally, Entities::get(entity) returns the correct EntityLocation if the entity exists.
let entity_ref = unsafe { EntityRef::new(self, entity, location) };
Some(entity_ref)
}

/// Returns an [`Entity`] iterator of current entities.
Expand All @@ -331,13 +334,16 @@ impl World {
.iter()
.enumerate()
.map(|(archetype_row, archetype_entity)| {
let entity = archetype_entity.entity();
let location = EntityLocation {
archetype_id: archetype.id(),
archetype_row: ArchetypeRow::new(archetype_row),
table_id: archetype.table_id(),
table_row: archetype_entity.table_row(),
};
EntityRef::new(self, archetype_entity.entity(), location)

// SAFETY: entity exists and location is validly constructed above
Copy link
Member

Choose a reason for hiding this comment

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

Can you be a bit more specific about how we know that the location is valid? Something like "location accurately specifies the archetype where the entity is stored" would be more clear IMO.

Copy link
Member

@joseph-gio joseph-gio Jan 16, 2023

Choose a reason for hiding this comment

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

Oops, this suggestion was slightly inaccurate. It's not just the archetype that matters -- a better way of phrasing it could be "location accurately specifies where the entity is stored".

Not sure how important this is, though.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think this basically covers it: this isn't a particularly tricky bit of unsafe. Feel free to make a follow-up PR if you think it's worthwhile though :)

unsafe { EntityRef::new(self, entity, location) }
})
})
}
Expand Down