diff --git a/benches/bench.rs b/benches/bench.rs index 685dbb0b..87fdaffe 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -66,7 +66,7 @@ fn iterate_100k(b: &mut Bencher) { world.spawn((Position(-(i as f32)), Velocity(i as f32))); } b.iter(|| { - for (_, (pos, vel)) in &mut world.query::<(&mut Position, &Velocity)>() { + for (_, (mut pos, vel)) in &mut world.query::<(&mut Position, &Velocity)>() { pos.0 += vel.0; } }) diff --git a/examples/ffa_simulation.rs b/examples/ffa_simulation.rs index 20dc6a4a..407000dc 100644 --- a/examples/ffa_simulation.rs +++ b/examples/ffa_simulation.rs @@ -62,7 +62,7 @@ fn batch_spawn_entities(world: &mut World, n: usize) { fn system_integrate_motion(world: &mut World) { let mut rng = thread_rng(); - for (id, (pos, s)) in &mut world.query::<(&mut Position, &Speed)>() { + for (id, (mut pos, s)) in &mut world.query::<(&mut Position, &Speed)>() { let change = (rng.gen_range(-s.0, s.0), rng.gen_range(-s.0, s.0)); pos.x += change.0; pos.y += change.1; @@ -72,7 +72,7 @@ fn system_integrate_motion(world: &mut World) { // In this system entities find the closest entity and fire at them fn system_fire_at_closest(world: &mut World) { - for (id0, (pos0, dmg0, kc0)) in + for (id0, (pos0, dmg0, mut kc0)) in &mut world.query::>() { // Find closest: diff --git a/src/archetype.rs b/src/archetype.rs index 1f8a9c7c..5bef240d 100644 --- a/src/archetype.rs +++ b/src/archetype.rs @@ -301,7 +301,7 @@ impl Archetype { } /// How, if at all, `Q` will access entities in this archetype - pub fn access(&self) -> Option { + pub fn access<'w, Q: Query<'w, C>, C: Copy + 'w>(&self) -> Option { Q::Fetch::access(self) } } diff --git a/src/borrow.rs b/src/borrow.rs index c42a4bb9..b65d1687 100644 --- a/src/borrow.rs +++ b/src/borrow.rs @@ -18,7 +18,7 @@ use core::ptr::NonNull; use core::sync::atomic::{AtomicUsize, Ordering}; use crate::archetype::Archetype; -use crate::{Component, MissingComponent}; +use crate::{Entity, MissingComponent, SmartComponent}; pub struct AtomicBorrow(AtomicUsize); @@ -63,15 +63,19 @@ const UNIQUE_BIT: usize = !(usize::max_value() >> 1); /// Shared borrow of an entity's component #[derive(Clone)] -pub struct Ref<'a, T: Component> { +pub struct Ref<'a, T: SmartComponent, C: Clone + 'a = ()> { archetype: &'a Archetype, target: NonNull, + entity: Entity, + context: C, } -impl<'a, T: Component> Ref<'a, T> { +impl<'a, T: SmartComponent, C: Clone + 'a> Ref<'a, T, C> { pub(crate) unsafe fn new( archetype: &'a Archetype, index: u32, + entity: Entity, + context: C, ) -> Result { let target = NonNull::new_unchecked( archetype @@ -81,36 +85,47 @@ impl<'a, T: Component> Ref<'a, T> { .add(index as usize), ); archetype.borrow::(); - Ok(Self { archetype, target }) + Ok(Self { + archetype, + target, + entity, + context, + }) } } -unsafe impl Send for Ref<'_, T> {} -unsafe impl Sync for Ref<'_, T> {} +unsafe impl, C: Clone + Sync> Send for Ref<'_, T, C> {} +unsafe impl, C: Clone + Sync> Sync for Ref<'_, T, C> {} -impl<'a, T: Component> Drop for Ref<'a, T> { +impl<'a, T: SmartComponent, C: Clone> Drop for Ref<'a, T, C> { fn drop(&mut self) { self.archetype.release::(); } } -impl<'a, T: Component> Deref for Ref<'a, T> { +impl<'a, T: SmartComponent, C: Clone> Deref for Ref<'a, T, C> { type Target = T; fn deref(&self) -> &T { - unsafe { self.target.as_ref() } + let value = unsafe { self.target.as_ref() }; + value.on_borrow(self.entity, self.context.clone()); + value } } /// Unique borrow of an entity's component -pub struct RefMut<'a, T: Component> { +pub struct RefMut<'a, T: SmartComponent, C: Clone = ()> { archetype: &'a Archetype, target: NonNull, + entity: Entity, + context: C, } -impl<'a, T: Component> RefMut<'a, T> { +impl<'a, T: SmartComponent, C: Clone> RefMut<'a, T, C> { pub(crate) unsafe fn new( archetype: &'a Archetype, index: u32, + entity: Entity, + context: C, ) -> Result { let target = NonNull::new_unchecked( archetype @@ -120,52 +135,72 @@ impl<'a, T: Component> RefMut<'a, T> { .add(index as usize), ); archetype.borrow_mut::(); - Ok(Self { archetype, target }) + Ok(Self { + archetype, + target, + entity, + context, + }) } } -unsafe impl Send for RefMut<'_, T> {} -unsafe impl Sync for RefMut<'_, T> {} +unsafe impl, C: Clone + Sync> Send for RefMut<'_, T, C> {} +unsafe impl, C: Clone + Sync> Sync for RefMut<'_, T, C> {} -impl<'a, T: Component> Drop for RefMut<'a, T> { +impl<'a, T: SmartComponent, C: Clone> Drop for RefMut<'a, T, C> { fn drop(&mut self) { self.archetype.release_mut::(); } } -impl<'a, T: Component> Deref for RefMut<'a, T> { +impl<'a, T: SmartComponent, C: Clone> Deref for RefMut<'a, T, C> { type Target = T; fn deref(&self) -> &T { - unsafe { self.target.as_ref() } + let value = unsafe { self.target.as_ref() }; + value.on_borrow(self.entity, self.context.clone()); + value } } -impl<'a, T: Component> DerefMut for RefMut<'a, T> { +impl<'a, T: SmartComponent, C: Clone> DerefMut for RefMut<'a, T, C> { fn deref_mut(&mut self) -> &mut T { - unsafe { self.target.as_mut() } + let value = unsafe { self.target.as_mut() }; + value.on_borrow_mut(self.entity, self.context.clone()); + value } } /// Handle to an entity with any component types #[derive(Copy, Clone)] -pub struct EntityRef<'a> { +pub struct EntityRef<'a, C: Clone = ()> { archetype: Option<&'a Archetype>, index: u32, + entity: Entity, + context: C, } -impl<'a> EntityRef<'a> { +impl<'a, C: Clone> EntityRef<'a, C> { /// Construct a `Ref` for an entity with no components - pub(crate) fn empty() -> Self { + pub(crate) fn empty(entity: Entity, context: C) -> Self { Self { archetype: None, index: 0, + entity, + context, } } - pub(crate) unsafe fn new(archetype: &'a Archetype, index: u32) -> Self { + pub(crate) unsafe fn new( + archetype: &'a Archetype, + index: u32, + entity: Entity, + context: C, + ) -> Self { Self { archetype: Some(archetype), index, + entity, + context, } } @@ -173,15 +208,31 @@ impl<'a> EntityRef<'a> { /// /// Panics if the component is already uniquely borrowed from another entity with the same /// components. - pub fn get(&self) -> Option> { - Some(unsafe { Ref::new(self.archetype?, self.index).ok()? }) + pub fn get>(&self) -> Option> { + Some(unsafe { + Ref::new( + self.archetype?, + self.index, + self.entity, + self.context.clone(), + ) + .ok()? + }) } /// Uniquely borrow the component of type `T`, if it exists /// /// Panics if the component is already borrowed from another entity with the same components. - pub fn get_mut(&self) -> Option> { - Some(unsafe { RefMut::new(self.archetype?, self.index).ok()? }) + pub fn get_mut>(&self) -> Option> { + Some(unsafe { + RefMut::new( + self.archetype?, + self.index, + self.entity, + self.context.clone(), + ) + .ok()? + }) } /// Enumerate the types of the entity's components @@ -197,5 +248,5 @@ impl<'a> EntityRef<'a> { } } -unsafe impl<'a> Send for EntityRef<'a> {} -unsafe impl<'a> Sync for EntityRef<'a> {} +unsafe impl<'a, C: Clone + Sync> Send for EntityRef<'a, C> {} +unsafe impl<'a, C: Clone + Sync> Sync for EntityRef<'a, C> {} diff --git a/src/lib.rs b/src/lib.rs index 8fcb2415..0eb48495 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,8 +31,8 @@ //! let a = world.spawn((123, true, "abc")); //! let b = world.spawn((42, false)); //! // Systems can be simple for loops -//! for (id, (number, &flag)) in world.query::<(&mut i32, &bool)>().iter() { -//! if flag { *number *= 2; } +//! for (id, (mut number, flag)) in world.query::<(&mut i32, &bool)>().iter() { +//! if *flag { *number *= 2; } //! } //! // Random access is simple and safe //! assert_eq!(*world.get::(a).unwrap(), 246); @@ -78,7 +78,9 @@ pub use entities::{Entity, NoSuchEntity}; pub use entity_builder::{BuiltEntity, EntityBuilder}; pub use query::{Access, BatchedIter, Query, QueryBorrow, QueryIter, With, Without}; pub use query_one::QueryOne; -pub use world::{ArchetypesGeneration, Component, ComponentError, Iter, SpawnBatchIter, World}; +pub use world::{ + ArchetypesGeneration, Component, ComponentError, Iter, SmartComponent, SpawnBatchIter, World, +}; // Unstable implementation details needed by the macros #[doc(hidden)] diff --git a/src/query.rs b/src/query.rs index e635e335..a6815cac 100644 --- a/src/query.rs +++ b/src/query.rs @@ -12,21 +12,23 @@ // See the License for the specific language governing permissions and // limitations under the License. +use core::fmt; use core::marker::PhantomData; +use core::ops::{Deref, DerefMut}; use core::ptr::NonNull; use crate::archetype::Archetype; use crate::entities::EntityMeta; -use crate::{Component, Entity}; +use crate::{Component, Entity, SmartComponent}; /// A collection of component types to fetch from a `World` -pub trait Query { +pub trait Query<'c, C: Clone + 'c = ()> { #[doc(hidden)] - type Fetch: for<'a> Fetch<'a>; + type Fetch: for<'q> Fetch<'q, 'c, C>; } /// Streaming iterators over contiguous homogeneous ranges of components -pub trait Fetch<'a>: Sized { +pub trait Fetch<'q, 'c, C: Clone + 'c>: Sized { /// Type of value to be fetched type Item; @@ -39,7 +41,7 @@ pub trait Fetch<'a>: Sized { /// /// # Safety /// `offset` must be in bounds of `archetype` - unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option; + unsafe fn get(archetype: &'q Archetype, offset: usize) -> Option; /// Release dynamic borrows acquired by `borrow` fn release(archetype: &Archetype); @@ -50,7 +52,7 @@ pub trait Fetch<'a>: Sized { /// - `release` must not be called while `'a` is still live /// - Bounds-checking must be performed externally /// - Any resulting borrows must be legal (e.g. no &mut to something another iterator might access) - unsafe fn next(&mut self) -> Self::Item; + unsafe fn next(&mut self, id: Entity, context: C) -> Self::Item; } /// Type of access a `Query` may have to an `Archetype` @@ -64,15 +66,48 @@ pub enum Access { Write, } -impl<'a, T: Component> Query for &'a T { +impl<'a, T: SmartComponent, C: Clone + 'a> Query<'a, C> for &'a T { type Fetch = FetchRead; } #[doc(hidden)] pub struct FetchRead(NonNull); -impl<'a, T: Component> Fetch<'a> for FetchRead { - type Item = &'a T; +pub struct Ref<'a, T, C> { + value: &'a T, + id: Entity, + context: C, +} + +impl<'a, T, C: Clone> Clone for Ref<'a, T, C> { + fn clone(&self) -> Self { + Self { + value: self.value, + id: self.id, + context: self.context.clone(), + } + } +} + +impl<'a, T, C: Copy> Copy for Ref<'a, T, C> {} + +impl<'a, T: fmt::Debug, C> fmt::Debug for Ref<'a, T, C> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.value.fmt(f) + } +} + +impl<'a, T: SmartComponent, C: Clone> Deref for Ref<'a, T, C> { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.value.on_borrow(self.id, self.context.clone()); + self.value + } +} + +impl<'q, 'c, T: SmartComponent, C: Clone + 'c> Fetch<'q, 'c, C> for FetchRead { + type Item = Ref<'q, T, C>; fn access(archetype: &Archetype) -> Option { if archetype.has::() { @@ -85,31 +120,65 @@ impl<'a, T: Component> Fetch<'a> for FetchRead { fn borrow(archetype: &Archetype) { archetype.borrow::(); } - unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { + + unsafe fn get(archetype: &'q Archetype, offset: usize) -> Option { archetype .get::() .map(|x| Self(NonNull::new_unchecked(x.as_ptr().add(offset)))) } + fn release(archetype: &Archetype) { archetype.release::(); } - unsafe fn next(&mut self) -> &'a T { + unsafe fn next(&mut self, id: Entity, context: C) -> Ref<'q, T, C> { let x = self.0.as_ptr(); self.0 = NonNull::new_unchecked(x.add(1)); - &*x + Ref { + value: &*x, + id, + context, + } } } -impl<'a, T: Component> Query for &'a mut T { +impl<'a, T: SmartComponent, C: Clone + 'a> Query<'a, C> for &'a mut T { type Fetch = FetchWrite; } #[doc(hidden)] pub struct FetchWrite(NonNull); -impl<'a, T: Component> Fetch<'a> for FetchWrite { - type Item = &'a mut T; +pub struct RefMut<'a, T, C> { + value: &'a mut T, + id: Entity, + context: C, +} + +impl<'a, T: fmt::Debug, C> fmt::Debug for RefMut<'a, T, C> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.value.fmt(f) + } +} + +impl<'a, T: SmartComponent, C: Clone> Deref for RefMut<'a, T, C> { + type Target = T; + + fn deref(&self) -> &Self::Target { + (&*self.value).on_borrow(self.id, self.context.clone()); + self.value + } +} + +impl<'a, T: SmartComponent, C: Clone> DerefMut for RefMut<'a, T, C> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.value.on_borrow_mut(self.id, self.context.clone()); + self.value + } +} + +impl<'q, 'c, T: SmartComponent, C: Clone + 'c> Fetch<'q, 'c, C> for FetchWrite { + type Item = RefMut<'q, T, C>; fn access(archetype: &Archetype) -> Option { if archetype.has::() { @@ -122,30 +191,36 @@ impl<'a, T: Component> Fetch<'a> for FetchWrite { fn borrow(archetype: &Archetype) { archetype.borrow_mut::(); } - unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { + + unsafe fn get(archetype: &'q Archetype, offset: usize) -> Option { archetype .get::() .map(|x| Self(NonNull::new_unchecked(x.as_ptr().add(offset)))) } + fn release(archetype: &Archetype) { archetype.release_mut::(); } - unsafe fn next(&mut self) -> &'a mut T { + unsafe fn next(&mut self, id: Entity, context: C) -> RefMut<'q, T, C> { let x = self.0.as_ptr(); self.0 = NonNull::new_unchecked(x.add(1)); - &mut *x + RefMut { + value: &mut *x, + id, + context, + } } } -impl Query for Option { +impl<'c, T: Query<'c, C>, C: Clone + 'c> Query<'c, C> for Option { type Fetch = TryFetch; } #[doc(hidden)] pub struct TryFetch(Option); -impl<'a, T: Fetch<'a>> Fetch<'a> for TryFetch { +impl<'q, 'c, T: Fetch<'q, 'c, C>, C: Clone + 'c> Fetch<'q, 'c, C> for TryFetch { type Item = Option; fn access(archetype: &Archetype) -> Option { @@ -155,15 +230,17 @@ impl<'a, T: Fetch<'a>> Fetch<'a> for TryFetch { fn borrow(archetype: &Archetype) { T::borrow(archetype) } - unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { + + unsafe fn get(archetype: &'q Archetype, offset: usize) -> Option { Some(Self(T::get(archetype, offset))) } + fn release(archetype: &Archetype) { T::release(archetype) } - unsafe fn next(&mut self) -> Option { - Some(self.0.as_mut()?.next()) + unsafe fn next(&mut self, id: Entity, context: C) -> Option { + Some(self.0.as_mut()?.next(id, context)) } } @@ -180,20 +257,22 @@ impl<'a, T: Fetch<'a>> Fetch<'a> for TryFetch { /// let c = world.spawn((42, "def")); /// let entities = world.query::>() /// .iter() -/// .map(|(e, &i)| (e, i)) +/// .map(|(e, i)| (e, *i)) /// .collect::>(); /// assert_eq!(entities, &[(c, 42)]); /// ``` pub struct Without(PhantomData<(Q, fn(T))>); -impl Query for Without { +impl<'c, T: Component, Q: Query<'c, C>, C: Clone + 'c> Query<'c, C> for Without { type Fetch = FetchWithout; } #[doc(hidden)] pub struct FetchWithout(F, PhantomData); -impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWithout { +impl<'q, 'c, T: Component, F: Fetch<'q, 'c, C>, C: Clone + 'c> Fetch<'q, 'c, C> + for FetchWithout +{ type Item = F::Item; fn access(archetype: &Archetype) -> Option { @@ -207,7 +286,7 @@ impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWithout { fn borrow(archetype: &Archetype) { F::borrow(archetype) } - unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { + unsafe fn get(archetype: &'q Archetype, offset: usize) -> Option { if archetype.has::() { return None; } @@ -217,8 +296,8 @@ impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWithout { F::release(archetype) } - unsafe fn next(&mut self) -> F::Item { - self.0.next() + unsafe fn next(&mut self, id: Entity, context: C) -> F::Item { + self.0.next(id, context) } } @@ -235,7 +314,7 @@ impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWithout { /// let c = world.spawn((42, "def")); /// let entities = world.query::>() /// .iter() -/// .map(|(e, &i)| (e, i)) +/// .map(|(e, i)| (e, *i)) /// .collect::>(); /// assert_eq!(entities.len(), 2); /// assert!(entities.contains(&(a, 123))); @@ -243,14 +322,16 @@ impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWithout { /// ``` pub struct With(PhantomData<(Q, fn(T))>); -impl Query for With { +impl<'c, T: Component, Q: Query<'c, C>, C: Clone + 'c> Query<'c, C> for With { type Fetch = FetchWith; } #[doc(hidden)] pub struct FetchWith(F, PhantomData); -impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWith { +impl<'q, 'c, T: Component, F: Fetch<'q, 'c, C>, C: Clone + 'c> Fetch<'q, 'c, C> + for FetchWith +{ type Item = F::Item; fn access(archetype: &Archetype) -> Option { @@ -264,37 +345,41 @@ impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWith { fn borrow(archetype: &Archetype) { F::borrow(archetype) } - unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { + + unsafe fn get(archetype: &'q Archetype, offset: usize) -> Option { if !archetype.has::() { return None; } Some(Self(F::get(archetype, offset)?, PhantomData)) } + fn release(archetype: &Archetype) { F::release(archetype) } - unsafe fn next(&mut self) -> F::Item { - self.0.next() + unsafe fn next(&mut self, id: Entity, context: C) -> F::Item { + self.0.next(id, context) } } /// A borrow of a `World` sufficient to execute the query `Q` /// /// Note that borrows are not released until this object is dropped. -pub struct QueryBorrow<'w, Q: Query> { +pub struct QueryBorrow<'w, Q: Query<'w, C>, C: Clone + 'w> { meta: &'w [EntityMeta], archetypes: &'w [Archetype], borrowed: bool, + context: C, _marker: PhantomData, } -impl<'w, Q: Query> QueryBorrow<'w, Q> { - pub(crate) fn new(meta: &'w [EntityMeta], archetypes: &'w [Archetype]) -> Self { +impl<'w, Q: Query<'w, C>, C: Clone + 'w> QueryBorrow<'w, Q, C> { + pub(crate) fn new(meta: &'w [EntityMeta], archetypes: &'w [Archetype], context: C) -> Self { Self { meta, archetypes, borrowed: false, + context, _marker: PhantomData, } } @@ -302,7 +387,7 @@ impl<'w, Q: Query> QueryBorrow<'w, Q> { /// Execute the query /// /// Must be called only once per query. - pub fn iter<'q>(&'q mut self) -> QueryIter<'q, 'w, Q> { + pub fn iter<'q>(&'q mut self) -> QueryIter<'q, 'w, Q, C> { self.borrow(); QueryIter { borrow: self, @@ -314,7 +399,7 @@ impl<'w, Q: Query> QueryBorrow<'w, Q> { /// Like `iter`, but returns child iterators of at most `batch_size` elements /// /// Useful for distributing work over a threadpool. - pub fn iter_batched<'q>(&'q mut self, batch_size: u32) -> BatchedIter<'q, 'w, Q> { + pub fn iter_batched<'q>(&'q mut self, batch_size: u32) -> BatchedIter<'q, 'w, Q, C> { self.borrow(); BatchedIter { borrow: self, @@ -356,12 +441,12 @@ impl<'w, Q: Query> QueryBorrow<'w, Q> { /// let entities = world.query::<&i32>() /// .with::() /// .iter() - /// .map(|(e, &i)| (e, i)) // Copy out of the world + /// .map(|(e, i)| (e, *i)) // Clone out of the world /// .collect::>(); /// assert!(entities.contains(&(a, 123))); /// assert!(entities.contains(&(b, 456))); /// ``` - pub fn with(self) -> QueryBorrow<'w, With> { + pub fn with(self) -> QueryBorrow<'w, With, C> { self.transform() } @@ -379,20 +464,21 @@ impl<'w, Q: Query> QueryBorrow<'w, Q> { /// let entities = world.query::<&i32>() /// .without::() /// .iter() - /// .map(|(e, &i)| (e, i)) // Copy out of the world + /// .map(|(e, i)| (e, *i)) // Clone out of the world /// .collect::>(); /// assert_eq!(entities, &[(c, 42)]); /// ``` - pub fn without(self) -> QueryBorrow<'w, Without> { + pub fn without(self) -> QueryBorrow<'w, Without, C> { self.transform() } /// Helper to change the type of the query - fn transform(mut self) -> QueryBorrow<'w, R> { + fn transform>(mut self) -> QueryBorrow<'w, R, C> { let x = QueryBorrow { meta: self.meta, archetypes: self.archetypes, borrowed: self.borrowed, + context: self.context.clone(), _marker: PhantomData, }; // Ensure `Drop` won't fire redundantly @@ -401,10 +487,10 @@ impl<'w, Q: Query> QueryBorrow<'w, Q> { } } -unsafe impl<'w, Q: Query> Send for QueryBorrow<'w, Q> {} -unsafe impl<'w, Q: Query> Sync for QueryBorrow<'w, Q> {} +unsafe impl<'w, Q: Query<'w, C>, C: Clone + Sync + 'w> Send for QueryBorrow<'w, Q, C> {} +unsafe impl<'w, Q: Query<'w, C>, C: Clone + Sync + 'w> Sync for QueryBorrow<'w, Q, C> {} -impl<'w, Q: Query> Drop for QueryBorrow<'w, Q> { +impl<'w, Q: Query<'w, C>, C: Clone + 'w> Drop for QueryBorrow<'w, Q, C> { fn drop(&mut self) { if self.borrowed { for x in self.archetypes { @@ -416,9 +502,9 @@ impl<'w, Q: Query> Drop for QueryBorrow<'w, Q> { } } -impl<'q, 'w, Q: Query> IntoIterator for &'q mut QueryBorrow<'w, Q> { - type Item = (Entity, >::Item); - type IntoIter = QueryIter<'q, 'w, Q>; +impl<'q, 'w, Q: Query<'w, C>, C: Clone + 'w> IntoIterator for &'q mut QueryBorrow<'w, Q, C> { + type Item = (Entity, >::Item); + type IntoIter = QueryIter<'q, 'w, Q, C>; fn into_iter(self) -> Self::IntoIter { self.iter() @@ -426,17 +512,17 @@ impl<'q, 'w, Q: Query> IntoIterator for &'q mut QueryBorrow<'w, Q> { } /// Iterator over the set of entities with the components in `Q` -pub struct QueryIter<'q, 'w, Q: Query> { - borrow: &'q mut QueryBorrow<'w, Q>, +pub struct QueryIter<'q, 'w, Q: Query<'w, C>, C: Clone + 'w> { + borrow: &'q mut QueryBorrow<'w, Q, C>, archetype_index: u32, - iter: Option>, + iter: Option>, } -unsafe impl<'q, 'w, Q: Query> Send for QueryIter<'q, 'w, Q> {} -unsafe impl<'q, 'w, Q: Query> Sync for QueryIter<'q, 'w, Q> {} +unsafe impl<'q, 'w, Q: Query<'w, C>, C: Clone + Sync + 'w> Send for QueryIter<'q, 'w, Q, C> {} +unsafe impl<'q, 'w, Q: Query<'w, C>, C: Clone + Sync + 'w> Sync for QueryIter<'q, 'w, Q, C> {} -impl<'q, 'w, Q: Query> Iterator for QueryIter<'q, 'w, Q> { - type Item = (Entity, >::Item); +impl<'q, 'w, Q: Query<'w, C>, C: Clone + 'w> Iterator for QueryIter<'q, 'w, Q, C> { + type Item = (Entity, >::Item); #[inline] fn next(&mut self) -> Option { @@ -450,24 +536,19 @@ impl<'q, 'w, Q: Query> Iterator for QueryIter<'q, 'w, Q> { entities: archetype.entities(), fetch, len: archetype.len(), + _context: PhantomData, }); } } - Some(ref mut iter) => match unsafe { iter.next() } { - None => { - self.iter = None; - continue; - } - Some((id, components)) => { - return Some(( - Entity { - id, - generation: self.borrow.meta[id as usize].generation, - }, - components, - )); + Some(ref mut iter) => { + match unsafe { iter.next(self.borrow.meta, self.borrow.context.clone()) } { + None => { + self.iter = None; + continue; + } + Some(item) => return Some(item), } - }, + } } } } @@ -478,7 +559,7 @@ impl<'q, 'w, Q: Query> Iterator for QueryIter<'q, 'w, Q> { } } -impl<'q, 'w, Q: Query> ExactSizeIterator for QueryIter<'q, 'w, Q> { +impl<'q, 'w, Q: Query<'w, C>, C: Clone + 'w> ExactSizeIterator for QueryIter<'q, 'w, Q, C> { fn len(&self) -> usize { self.borrow .archetypes @@ -489,38 +570,48 @@ impl<'q, 'w, Q: Query> ExactSizeIterator for QueryIter<'q, 'w, Q> { } } -struct ChunkIter { +struct ChunkIter<'c, Q: Query<'c, C>, C: Clone + 'c> { entities: NonNull, fetch: Q::Fetch, len: u32, + _context: PhantomData<*const C>, } -impl ChunkIter { +impl<'c, Q: Query<'c, C>, C: Clone + 'c> ChunkIter<'c, Q, C> { #[inline] - unsafe fn next<'a>(&mut self) -> Option<(u32, >::Item)> { + unsafe fn next<'a>( + &mut self, + meta: &'c [EntityMeta], + context: C, + ) -> Option<(Entity, >::Item)> { if self.len == 0 { return None; } self.len -= 1; - let entity = self.entities.as_ptr(); - self.entities = NonNull::new_unchecked(entity.add(1)); - Some((*entity, self.fetch.next())) + let entity_ptr = self.entities.as_ptr(); + self.entities = NonNull::new_unchecked(entity_ptr.add(1)); + let id = *entity_ptr; + let entity = Entity { + id, + generation: meta[id as usize].generation, + }; + Some((entity, self.fetch.next(entity, context))) } } /// Batched version of `QueryIter` -pub struct BatchedIter<'q, 'w, Q: Query> { - borrow: &'q mut QueryBorrow<'w, Q>, +pub struct BatchedIter<'q, 'w, Q: Query<'w, C>, C: Clone + 'w> { + borrow: &'q mut QueryBorrow<'w, Q, C>, archetype_index: u32, batch_size: u32, batch: u32, } -unsafe impl<'q, 'w, Q: Query> Send for BatchedIter<'q, 'w, Q> {} -unsafe impl<'q, 'w, Q: Query> Sync for BatchedIter<'q, 'w, Q> {} +unsafe impl<'q, 'w, Q: Query<'w, C>, C: Clone + Sync + 'w> Send for BatchedIter<'q, 'w, Q, C> {} +unsafe impl<'q, 'w, Q: Query<'w, C>, C: Clone + Sync + 'w> Sync for BatchedIter<'q, 'w, Q, C> {} -impl<'q, 'w, Q: Query> Iterator for BatchedIter<'q, 'w, Q> { - type Item = Batch<'q, 'w, Q>; +impl<'q, 'w, Q: Query<'w, C>, C: Clone + 'w> Iterator for BatchedIter<'q, 'w, Q, C> { + type Item = Batch<'q, 'w, Q, C>; fn next(&mut self) -> Option { loop { @@ -544,7 +635,9 @@ impl<'q, 'w, Q: Query> Iterator for BatchedIter<'q, 'w, Q> { }, fetch, len: self.batch_size.min(archetype.len() - offset), + _context: PhantomData, }, + context: self.borrow.context.clone(), }); } else { self.archetype_index += 1; @@ -559,33 +652,27 @@ impl<'q, 'w, Q: Query> Iterator for BatchedIter<'q, 'w, Q> { } /// A sequence of entities yielded by `BatchedIter` -pub struct Batch<'q, 'w, Q: Query> { +pub struct Batch<'q, 'w, Q: Query<'w, C>, C: Clone + 'w> { _marker: PhantomData<&'q ()>, meta: &'w [EntityMeta], - state: ChunkIter, + state: ChunkIter<'w, Q, C>, + context: C, } -impl<'q, 'w, Q: Query> Iterator for Batch<'q, 'w, Q> { - type Item = (Entity, >::Item); +impl<'q, 'w, Q: Query<'w, C>, C: Clone + 'w> Iterator for Batch<'q, 'w, Q, C> { + type Item = (Entity, >::Item); fn next(&mut self) -> Option { - let (id, components) = unsafe { self.state.next()? }; - Some(( - Entity { - id, - generation: self.meta[id as usize].generation, - }, - components, - )) + unsafe { self.state.next(self.meta, self.context.clone()) } } } -unsafe impl<'q, 'w, Q: Query> Send for Batch<'q, 'w, Q> {} -unsafe impl<'q, 'w, Q: Query> Sync for Batch<'q, 'w, Q> {} +unsafe impl<'q, 'w, Q: Query<'w, C>, C: Clone + Sync + 'w> Send for Batch<'q, 'w, Q, C> {} +unsafe impl<'q, 'w, Q: Query<'w, C>, C: Clone + Sync + 'w> Sync for Batch<'q, 'w, Q, C> {} macro_rules! tuple_impl { ($($name: ident),*) => { - impl<'a, $($name: Fetch<'a>),*> Fetch<'a> for ($($name,)*) { + impl<'a, 'z, Z: Clone + 'z, $($name: Fetch<'a, 'z, Z>),*> Fetch<'a, 'z, Z> for ($($name,)*) { type Item = ($($name::Item,)*); #[allow(unused_variables, unused_mut)] @@ -601,23 +688,26 @@ macro_rules! tuple_impl { fn borrow(archetype: &Archetype) { $($name::borrow(archetype);)* } + #[allow(unused_variables)] unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { Some(($($name::get(archetype, offset)?,)*)) } + #[allow(unused_variables)] fn release(archetype: &Archetype) { $($name::release(archetype);)* } - unsafe fn next(&mut self) -> Self::Item { + #[allow(unused_variables)] + unsafe fn next(&mut self, id: Entity, context: Z) -> Self::Item { #[allow(non_snake_case)] let ($($name,)*) = self; - ($($name.next(),)*) + ($($name.next(id, context.clone()),)*) } } - impl<$($name: Query),*> Query for ($($name,)*) { + impl<'z, Z: Clone + 'z, $($name: Query<'z, Z>),*> Query<'z, Z> for ($($name,)*) { type Fetch = ($($name::Fetch,)*); } }; diff --git a/src/query_one.rs b/src/query_one.rs index 3e609a31..952f8a7a 100644 --- a/src/query_one.rs +++ b/src/query_one.rs @@ -1,27 +1,36 @@ use core::marker::PhantomData; use crate::query::{Fetch, With, Without}; -use crate::{Archetype, Component, Query}; +use crate::{Archetype, Component, Entity, Query}; /// A borrow of a `World` sufficient to execute the query `Q` on a single entity -pub struct QueryOne<'a, Q: Query> { - archetype: &'a Archetype, +pub struct QueryOne<'w, Q: Query<'w, C>, C: Copy + 'w> { + archetype: &'w Archetype, index: u32, borrowed: bool, + entity: Entity, + context: C, _marker: PhantomData, } -impl<'a, Q: Query> QueryOne<'a, Q> { +impl<'w, Q: Query<'w, C>, C: Copy + 'w> QueryOne<'w, Q, C> { /// Construct a query accessing the entity in `archetype` at `index` /// /// # Safety /// /// `index` must be in-bounds for `archetype` - pub(crate) unsafe fn new(archetype: &'a Archetype, index: u32) -> Self { + pub(crate) unsafe fn new( + archetype: &'w Archetype, + index: u32, + entity: Entity, + context: C, + ) -> Self { Self { archetype, index, borrowed: false, + entity, + context, _marker: PhantomData, } } @@ -32,7 +41,7 @@ impl<'a, Q: Query> QueryOne<'a, Q> { /// /// Panics if called more than once or if it would construct a borrow that clashes with another /// pre-existing borrow. - pub fn get(&mut self) -> Option<>::Item> { + pub fn get(&mut self) -> Option<>::Item> { if self.borrowed { panic!("called QueryOnce::get twice; construct a new query instead"); } @@ -40,30 +49,32 @@ impl<'a, Q: Query> QueryOne<'a, Q> { let mut fetch = Q::Fetch::get(self.archetype, self.index as usize)?; self.borrowed = true; Q::Fetch::borrow(self.archetype); - Some(fetch.next()) + Some(fetch.next(self.entity, self.context)) } } /// Transform the query into one that requires a certain component without borrowing it /// /// See `QueryBorrow::with` for details. - pub fn with(self) -> QueryOne<'a, With> { + pub fn with(self) -> QueryOne<'w, With, C> { self.transform() } /// Transform the query into one that skips entities having a certain component /// /// See `QueryBorrow::without` for details. - pub fn without(self) -> QueryOne<'a, Without> { + pub fn without(self) -> QueryOne<'w, Without, C> { self.transform() } /// Helper to change the type of the query - fn transform(mut self) -> QueryOne<'a, R> { + fn transform>(mut self) -> QueryOne<'w, R, C> { let x = QueryOne { archetype: self.archetype, index: self.index, borrowed: self.borrowed, + entity: self.entity, + context: self.context, _marker: PhantomData, }; // Ensure `Drop` won't fire redundantly @@ -72,7 +83,7 @@ impl<'a, Q: Query> QueryOne<'a, Q> { } } -impl Drop for QueryOne<'_, Q> { +impl<'w, Q: Query<'w, C>, C: Copy + 'w> Drop for QueryOne<'w, Q, C> { fn drop(&mut self) { if self.borrowed { Q::Fetch::release(self.archetype); @@ -80,5 +91,5 @@ impl Drop for QueryOne<'_, Q> { } } -unsafe impl Send for QueryOne<'_, Q> {} -unsafe impl Sync for QueryOne<'_, Q> {} +unsafe impl<'w, Q: Query<'w, C>, C: Copy + Sync + 'w> Send for QueryOne<'w, Q, C> {} +unsafe impl<'w, Q: Query<'w, C>, C: Copy + Sync + 'w> Sync for QueryOne<'w, Q, C> {} diff --git a/src/world.rs b/src/world.rs index 6196c07b..3b7977fd 100644 --- a/src/world.rs +++ b/src/world.rs @@ -205,7 +205,7 @@ impl World { self.entities.contains(entity) } - /// Efficiently iterate over all entities that have certain components + /// Efficiently iterate over all entities that have certain components. /// /// Calling `iter` on the returned value yields `(Entity, Q)` tuples, where `Q` is some query /// type. A query type is `&T`, `&mut T`, a tuple of query types, or an `Option` wrapping a @@ -229,6 +229,12 @@ impl World { /// its dynamic borrows from the world to be released. Similarly, lifetime rules ensure that /// references obtained from a query cannot outlive the `QueryBorrow`. /// + /// If you need change detection of some sort, you will likely want to use [`query_with_context`] + /// instead; this method is actually shorthand for `.query_with_context(())`. See also [`SmartComponent`]. + /// + /// [`query_with_context`]: World::query_with_context + /// [`SmartComponent`]: SmartComponent + /// /// # Example /// ``` /// # use hecs::*; @@ -238,14 +244,29 @@ impl World { /// let c = world.spawn((42, "def")); /// let entities = world.query::<(&i32, &bool)>() /// .iter() - /// .map(|(e, (&i, &b))| (e, i, b)) // Copy out of the world + /// .map(|(e, (i, b))| (e, *i, *b)) // Copy out of the world /// .collect::>(); /// assert_eq!(entities.len(), 2); /// assert!(entities.contains(&(a, 123, true))); /// assert!(entities.contains(&(b, 456, false))); /// ``` - pub fn query(&self) -> QueryBorrow<'_, Q> { - QueryBorrow::new(&self.entities.meta, &self.archetypes) + pub fn query<'q, Q: Query<'q>>(&'q self) -> QueryBorrow<'q, Q, ()> { + self.query_with_context(()) + } + + /// See [`query`]. This method augments `query` with a contex type, accessible by components + /// through the [`SmartComponent`] trait (`query` is actually implemented on top of this + /// method, using the empty context `()`.) If you are implementing an ECS on top of `hecs` + /// which needs some sort of change detection, this is likely the interface you want to use. + /// + /// [`query`]: World::query + /// [`SmartComponent`]: SmartComponent + pub fn query_with_context<'q, Q, C>(&'q self, context: C) -> QueryBorrow<'q, Q, C> + where + Q: Query<'q, C>, + C: Copy + 'q, + { + QueryBorrow::new(&self.entities.meta, &self.archetypes, context) } /// Prepare a query against a single entity @@ -256,6 +277,12 @@ impl World { /// /// Handy for accessing multiple components simultaneously. /// + /// If you need change detection of some sort, you will likely want to use [`query_one_with_context`] + /// instead; this method is actually shorthand for `.query_one_with_context(())`. See also [`SmartComponent`]. + /// + /// [`query_one_with_context`]: World::query_with_context + /// [`SmartComponent`]: SmartComponent + /// /// # Example /// ``` /// # use hecs::*; @@ -263,13 +290,42 @@ impl World { /// let a = world.spawn((123, true, "abc")); /// // The returned query must outlive the borrow made by `get` /// let mut query = world.query_one::<(&mut i32, &bool)>(a).unwrap(); - /// let (number, flag) = query.get().unwrap(); + /// let (mut number, flag) = query.get().unwrap(); /// if *flag { *number *= 2; } /// assert_eq!(*number, 246); /// ``` - pub fn query_one(&self, entity: Entity) -> Result, NoSuchEntity> { + pub fn query_one<'q, Q>(&'q self, entity: Entity) -> Result, NoSuchEntity> + where + Q: Query<'q>, + { + self.query_one_with_context(entity, ()) + } + + /// See [`query_one`]. This method augments `query_one` with a contex type, accessible by components + /// through the [`SmartComponent`] trait (`query_one` is actually implemented on top of this + /// method, using the empty context `()`.) If you are implementing an ECS on top of `hecs` + /// which needs some sort of change detection, this is likely the interface you want to use. + /// + /// [`query_one`]: World::query_one + /// [`SmartComponent`]: SmartComponent + pub fn query_one_with_context<'q, Q, C>( + &'q self, + entity: Entity, + context: C, + ) -> Result, NoSuchEntity> + where + Q: Query<'q, C>, + C: Copy + 'q, + { let loc = self.entities.get(entity)?; - Ok(unsafe { QueryOne::new(&self.archetypes[loc.archetype as usize], loc.index) }) + Ok(unsafe { + QueryOne::new( + &self.archetypes[loc.archetype as usize], + loc.index, + entity, + context, + ) + }) } /// Borrow the `T` component of `entity` @@ -277,31 +333,94 @@ impl World { /// Panics if the component is already uniquely borrowed from another entity with the same /// components. pub fn get(&self, entity: Entity) -> Result, ComponentError> { + self.get_with_context(entity, ()) + } + + /// Uniquely borrow the `T` component of `entity` + /// + /// Panics if the component is already borrowed from another entity with the same components. + pub fn get_mut(&self, entity: Entity) -> Result, ComponentError> { + self.get_mut_with_context(entity, ()) + } + + /// Access an entity regardless of its component types + /// + /// Does not immediately borrow any component. + pub fn entity(&self, entity: Entity) -> Result, NoSuchEntity> { + self.entity_with_context(entity, ()) + } + + /// See [`get`]. This method allows `get` to be augmented with a context type `C`, provided to + /// components which implement `SmartComponent`. This allows for detecting when the component + /// is accessed immutably. See also [`SmartComponent`]. + /// + /// [`get`]: World::get + /// [`SmartComponent`]: SmartComponent + pub fn get_with_context, C: Clone>( + &self, + entity: Entity, + context: C, + ) -> Result, ComponentError> { let loc = self.entities.get(entity)?; if loc.archetype == 0 { return Err(MissingComponent::new::().into()); } - Ok(unsafe { Ref::new(&self.archetypes[loc.archetype as usize], loc.index)? }) + Ok(unsafe { + Ref::new( + &self.archetypes[loc.archetype as usize], + loc.index, + entity, + context, + )? + }) } - /// Uniquely borrow the `T` component of `entity` + /// See [`get_mut`]. This method allows `get_mut` to be augmented with a context type `C`, provided to + /// components which implement `SmartComponent`. This allows for detecting when the component + /// is accessed mutably. See also [`SmartComponent`]. /// - /// Panics if the component is already borrowed from another entity with the same components. - pub fn get_mut(&self, entity: Entity) -> Result, ComponentError> { + /// [`get_mut`]: World::get_mut + /// [`SmartComponent`]: SmartComponent + pub fn get_mut_with_context, C: Clone>( + &self, + entity: Entity, + context: C, + ) -> Result, ComponentError> { let loc = self.entities.get(entity)?; if loc.archetype == 0 { return Err(MissingComponent::new::().into()); } - Ok(unsafe { RefMut::new(&self.archetypes[loc.archetype as usize], loc.index)? }) + Ok(unsafe { + RefMut::new( + &self.archetypes[loc.archetype as usize], + loc.index, + entity, + context, + )? + }) } - /// Access an entity regardless of its component types + /// See [`entity`]. This method allows `entity` to be augmented with a context type `C`, provided to + /// components which implement `SmartComponent`. This allows for detecting when the component is + /// accessed, and how. See also [`SmartComponent`]. /// - /// Does not immediately borrow any component. - pub fn entity(&self, entity: Entity) -> Result, NoSuchEntity> { + /// [`entity`]: World::entity + /// [`SmartComponent`]: SmartComponent + pub fn entity_with_context( + &self, + entity: Entity, + context: C, + ) -> Result, NoSuchEntity> { Ok(match self.entities.get(entity)? { - Location { archetype: 0, .. } => EntityRef::empty(), - loc => unsafe { EntityRef::new(&self.archetypes[loc.archetype as usize], loc.index) }, + Location { archetype: 0, .. } => EntityRef::empty(entity, context), + loc => unsafe { + EntityRef::new( + &self.archetypes[loc.archetype as usize], + loc.index, + entity, + context, + ) + }, }) } @@ -645,6 +764,26 @@ impl From for ComponentError { pub trait Component: Send + Sync + 'static {} impl Component for T {} +/// `SmartComponent` is an additional layer on top of `Component` which optionally allows components +/// to be aware of when they are accessed. It is by default implemented for all components w.r.t. the +/// "empty context" `()` (all types that implement `Component` implement `SmartComponent<()>`.) +/// +/// The callbacks in this trait are called *when the reference returned from a query is dereferenced;* +/// in other words, these will not be called until the component is derefenced to an `&` or `&mut`. +/// +/// The type parameter `T` of `SmartComponent` is the "context" type. It's generally expected that +/// you'll use an immutable reference of some kind here, but anything clonable is fine. +pub trait SmartComponent: Component { + /// Called when the component is immutably accessed. You probably won't ever need this one. + fn on_borrow(&self, _id: Entity, _x: T) {} + /// Called when the component is mutably accessed. Useful for flagging changes to components. + /// For example, the context type `T` might be a `Mutex` type or similar (`AtomicBitSet`) + /// for which you might set the relevant bit on a mutable access to this component on an entity. + fn on_borrow_mut(&mut self, _id: Entity, _x: T) {} +} + +impl SmartComponent<()> for T {} + /// Iterator over all of a world's entities pub struct Iter<'a> { archetypes: core::slice::Iter<'a, Archetype>, @@ -684,13 +823,13 @@ impl<'a> Iterator for Iter<'a> { let index = self.index; self.index += 1; let id = current.entity_id(index); - return Some(( - Entity { - id, - generation: self.entities.meta[id as usize].generation, - }, - unsafe { EntityRef::new(current, index) }, - )); + let entity = Entity { + id, + generation: self.entities.meta[id as usize].generation, + }; + return Some((entity, unsafe { + EntityRef::new(current, index, entity, ()) + })); } } } diff --git a/tests/tests.rs b/tests/tests.rs index 22b62a9d..22ae8ce2 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -50,7 +50,7 @@ fn query_all() { let ents = world .query::<(&i32, &&str)>() .iter() - .map(|(e, (&i, &s))| (e, i, s)) + .map(|(e, (i, s))| (e, *i, *s)) .collect::>(); assert_eq!(ents.len(), 2); assert!(ents.contains(&(e, 123, "abc"))); @@ -70,7 +70,7 @@ fn query_single_component() { let ents = world .query::<&i32>() .iter() - .map(|(e, &i)| (e, i)) + .map(|(e, i)| (e, *i)) .collect::>(); assert_eq!(ents.len(), 2); assert!(ents.contains(&(e, 123))); @@ -93,7 +93,7 @@ fn query_sparse_component() { let ents = world .query::<&bool>() .iter() - .map(|(e, &b)| (e, b)) + .map(|(e, b)| (e, *b)) .collect::>(); assert_eq!(ents, &[(f, true)]); } @@ -106,7 +106,7 @@ fn query_optional_component() { let ents = world .query::<(Option<&bool>, &i32)>() .iter() - .map(|(e, (b, &i))| (e, b.copied(), i)) + .map(|(e, (b, i))| (e, b.as_deref().copied(), *i)) .collect::>(); assert_eq!(ents.len(), 2); assert!(ents.contains(&(e, None, 123))); @@ -139,7 +139,7 @@ fn dynamic_components() { world .query::<(&i32, &bool)>() .iter() - .map(|(e, (&i, &b))| (e, i, b)) + .map(|(e, (i, b))| (e, *i, *b)) .collect::>(), &[(e, 42, true)] ); @@ -148,7 +148,7 @@ fn dynamic_components() { world .query::<(&i32, &bool)>() .iter() - .map(|(e, (&i, &b))| (e, i, b)) + .map(|(e, (i, b))| (e, *i, *b)) .collect::>(), &[] ); @@ -156,7 +156,7 @@ fn dynamic_components() { world .query::<(&bool, &&str)>() .iter() - .map(|(e, (&b, &s))| (e, b, s)) + .map(|(e, (b, s))| (e, *b, *s)) .collect::>(), &[(e, true, "abc")] ); @@ -331,7 +331,7 @@ fn spawn_batch() { let entities = world .query::<&i32>() .iter() - .map(|(_, &x)| x) + .map(|(_, x)| *x) .collect::>(); assert_eq!(entities.len(), 100); } @@ -342,12 +342,22 @@ fn query_one() { let a = world.spawn(("abc", 123)); let b = world.spawn(("def", 456)); let c = world.spawn(("ghi", 789, true)); - assert_eq!(world.query_one::<&i32>(a).unwrap().get(), Some(&123)); - assert_eq!(world.query_one::<&i32>(b).unwrap().get(), Some(&456)); + assert_eq!( + world.query_one::<&i32>(a).unwrap().get().as_deref(), + Some(&123) + ); + assert_eq!( + world.query_one::<&i32>(b).unwrap().get().as_deref(), + Some(&456) + ); assert!(world.query_one::<(&i32, &bool)>(a).unwrap().get().is_none()); assert_eq!( - world.query_one::<(&i32, &bool)>(c).unwrap().get(), - Some((&789, &true)) + world + .query_one::<(&i32, &bool)>(c) + .unwrap() + .get() + .map(|(x, y)| (*x, *y)), + Some((789, true)) ); world.despawn(a).unwrap(); assert!(world.query_one::<&i32>(a).is_err());