Authors: @doot Contributors: @bushRAT @quartermeister @Diddykonga @Victoron
This document details an alternative design to Unified EntityPtr Types.
We have a few types within Bevy ECS that allow access to an Entity's metadata and components:
EntityRef
: provides read-only access.EntityMut
: provides mutable access.EntityWorldMut
: provides structurally mutable access, like adding/removing components or despawning.However, we incur API duplication because these are unique types even though they only differ in semantics (they are essentially layout and alignment-compatible). If we can either 1. safely treat them as a single type for certain operations, or 2. eliminate the need for explicit conversions, we can reduce this duplication. This document attempts #2.
EntityView
To reduce API duplication we can try to implement a Deref "ladder" following: EntityWorldMut
-> EntityMut
-> EntityRef
. However, that creates a soundness issue that we'll describe below:
#[derive(Copy, Clone)] // Remember that we can currently copy around EntityRefs
pub struct EntityRef<'w>(UnsafeEntityCell<'w>);
pub struct EntityMut<'w>(UnsafeEntityCell<'w>);
// We can deref from &'a EntityMut<'w> to &'a EntityRef<'w>
// So if we then clone the EntityRef we would now have an owned EntityRef<'w>.
// This would untie the lifetime of the EntityRef from the EntityMut,
// which means we could still mess with the EntityMut mutably while reading
// from the EntityRef. That is aliased mutability aka Undefined Behavior (UB)!
impl<'w> Deref for EntityMut<'w> {
type Target = EntityRef<'w>;
fn deref(&self) -> &Self::Target;
}
So, enter EntityView
:
pub struct EntityView<'w>(UnsafeEntityCell<'w>);
impl<'w> EntityView<'w> {
// Only read operations are allowed, no mutations
pub fn id(&self) -> Entity;
pub fn contains<T: Component>(&self) -> bool;
pub fn get<T: Component>(&self) -> Option<&'_ T>;
pub fn components<Q: ReadOnlyQueryData>(&self) -> Q::Item<'_>;
// ...
}
It's almost exactly what EntityRef
currently is except it doesn't impl Clone
or Copy
, which means we can deref EntityMut
into it without causing UB. To keep things simple we'll also deref EntityRef
into it, which leaves us with two separate Deref
"ladders":
EntityWorldMut
-> EntityMut
-> EntityView
EntityRef
-> EntityView
And if users want to go from EntityMut
-> EntityRef
or EntityRef
<-> EntityView
, we'll still provide From
/Into
implementations for those conversions. However, those follow a &'a EntityMut<'w>
-> EntityRef<'a>
conversion (i.e. following the current lifetime shortening we use) so we don't introduce any UB. See the Public API Definition below for a more complete look.
The TL;DR is that impl<'w> Deref for EntityMut<'w> { type Target = EntityRef<'w>; }
is unsound because impl Clone for EntityRef
so we need a new type that's like EntityRef
but doesn't implement Clone
or Copy
.
#[repr(transparent)]
pub struct EntityView<'w>(UnsafeEntityCell<'w>);
impl<'w> EntityView<'w> {
pub fn id(&self) -> Entity;
pub fn location(&self) -> EntityLocation;
pub fn archetype(&self) -> &Archetype;
pub fn contains<T: Component>(&self) -> bool;
pub fn contains_id(&self, component_id: ComponentId) -> bool;
pub fn contains_type_id(&self, type_id: TypeId) -> bool;
pub fn get<T: Component>(&self) -> Option<&'_ T>;
pub fn get_ref<T: Component>(&self) -> Option<Ref<'_, T>>;
pub fn get_change_ticks<T: Component>(&self) -> Option<ComponentTicks>;
pub fn get_change_ticks_by_id(&self, component_id: ComponentId) -> Option<ComponentTicks>;
pub fn get_by_id<F: DynamicComponentFetch>(&self, component_ids: F) -> Result<F::Ref<'_>, EntityComponentError>;
pub fn components<Q: ReadOnlyQueryData>(&self) -> Q::Item<'_>;
pub fn get_components<Q: ReadOnlyQueryData>(&self) -> Option<Q::Item<'_>>;
}
impl<'w> From<EntityRef<'w>> for EntityView<'w> {}
impl<'w> From<EntityMut<'w>> for EntityView<'w> {}
impl<'w> From<EntityWorldMut<'w>> for EntityView<'w> {}
impl<'a> From<&'a EntityView<'_>> for EntityView<'a> {}
impl<'a> From<&'a EntityRef<'_>> for EntityView<'a> {}
impl<'a> From<&'a EntityMut<'_>> for EntityView<'a> {}
impl<'a> From<&'a EntityWorldMut<'w>> for EntityView<'a> {}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct EntityRef<'w>(UnsafeEntityCell<'w>);
impl<'w> EntityRef<'w> {
pub fn get<T: Component>(&self) -> Option<&'w T>;
pub fn get_ref<T: Component>(&self) -> Option<Ref<'w, T>>;
pub fn get_by_id<F: DynamicComponentFetch>(&self, component_ids: F) -> Result<F::Ref<'w>, EntityComponentError>;
pub fn components<Q: ReadOnlyQueryData>(&self) -> Q::Item<'w>;
pub fn get_components<Q: ReadOnlyQueryData>(&self) -> Option<Q::Item<'w>>;
}
impl<'w> Deref for EntityRef<'w> {
type Target = EntityView<'w>;
fn deref(&self) -> &Self::Target;
}
impl<'w> From<EntityView<'w>> for EntityRef<'w> {}
impl<'w> From<EntityMut<'w>> for EntityRef<'w> {}
impl<'w> From<EntityWorldMut<'w>> for EntityRef<'w> {}
impl<'w> From<&'a EntityView<'_>> for EntityRef<'a> {}
impl<'w> From<&'a EntityRef<'_>> for EntityRef<'a> {}
impl<'a> From<&'a EntityMut<'_>> for EntityRef<'a> {}
impl<'a> From<&'a EntityWorldMut<'w>> for EntityRef<'a> {}
#[repr(transparent)]
pub struct EntityMut<'w>(UnsafeEntityCell<'w>);
impl<'w> EntityMut<'w> {
pub fn reborrow(&mut self) -> EntityMut<'_>;
pub fn as_readonly(&self) -> EntityRef<'_>;
pub fn into_borrow<T: Component>(self) -> Option<&'w T>;
pub fn into_ref<T: Component>(self) -> Option<Ref<'w, T>>;
pub fn get_mut<T: Component>(&mut self) -> Option<Mut<'_, T>>;
pub fn into_mut<T: Component>(self) -> Option<Mut<'w, T>>;
pub fn into_borrow_by_id<F: DynamicComponentFetch>(self, component_ids: F) -> Result<F::Ref<'w>, EntityComponentError>;
pub fn get_mut_by_id<F: DynamicComponentFetch>(&mut self, component_ids: F) -> Result<F::Mut<'_>, EntityComponentError>;
pub unsafe fn get_mut_by_id_unchecked<F: DynamicComponentFetch>(&self, component_ids: F) -> Result<F::Mut<'_>, EntityComponentError>;
pub fn into_mut_by_id<F: DynamicComponentFetch>(self, component_ids: F) -> Result<F::Mut<'w>, EntityComponentError>;
pub fn into_components<Q: ReadOnlyQueryData>(self) -> Q::Item<'w>;
pub fn try_into_components<Q: ReadOnlyQueryData>(self) -> Option<Q::Item<'w>>;
}
impl<'w> Deref for EntityMut<'w> {
type Target = EntityView<'w>;
fn deref(&self) -> &Self::Target;
}
impl<'w> From<EntityWorldMut<'w>> for EntityMut<'w> {}
impl<'a> From<&'a mut EntityMut<'_>> for EntityMut<'a> {}
impl<'a> From<&'a mut EntityWorldMut<'_>> for EntityMut<'a> {}
#[repr(transparent)]
pub struct EntityWorldMut<'w>(UnsafeEntityCell<'w>);
impl<'w> EntityWorldMut<'w> {
pub fn insert<T: Bundle>(&mut self, bundle: T);
pub fn insert_if_new<T: Bundle>(&mut self, bundle: T) -> &mut Self;
pub unsafe fn insert_by_id(&mut self, component_id: ComponentId, component: OwningPtr<'_>) -> &mut Self;
pub unsafe fn insert_by_ids<'a, I: Iterator<Item = OwningPtr<'a>>>(&mut self, component_ids: &[ComponentId], iter_components: I) -> &mut Self;
pub fn take<T: Bundle>(&mut self) -> Option<T>;
pub fn remove<T: Bundle>(&mut self) -> &mut Self;
pub fn remove_with_requires<T: Bundle>(&mut self) -> &mut Self;
pub fn retain<T: Bundle>(&mut self) -> &mut Self;
pub fn remove_by_id(&mut self, component_id: ComponentId) -> &mut Self;
pub fn clear(&mut self) -> &mut Self;
pub fn despawn(self);
pub fn flush(self) -> Entity;
pub fn world(&self) -> &World;
pub unsafe fn world_mut(&mut self) -> &mut World;
pub fn into_world_mut(self) -> &'w mut World;
pub fn world_scope<U>(&mut self, f: impl FnOnce(&mut World) -> U) -> U;
pub fn update_location(&mut self);
pub fn entry<'a, T: Component>(&'a mut self) -> Entry<'w, 'a, T>;
pub fn trigger(&mut self, event: impl Event) -> &mut Self;
pub fn observe<E: Event, B: Bundle, M>(&mut self, observer: impl IntoObserverSystem<E, B, M>) -> &mut Self;
}
impl<'w> Deref for EntityWorldMut<'w> {
type Target = EntityMut<'w>;
fn deref(&self) -> &Self::Target;
}
impl<'w> DerefMut for EntityWorldMut<'w> {
fn deref_mut(&mut self) -> &mut Self::Target;
}