From 86cbdc7becc18a6dcbc5ece5dfff09e0b8f7cd6f Mon Sep 17 00:00:00 2001 From: Jonathan Spira Date: Thu, 21 Mar 2024 22:48:14 -0400 Subject: [PATCH 1/6] view on world --- src/lib.rs | 4 +-- src/query.rs | 72 ++++++++++++++++++++++++++++++++++++++++++++++++- src/world.rs | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 148 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 53c6646e..c2b660b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -97,8 +97,8 @@ pub use entity_builder::{BuiltEntity, BuiltEntityClone, EntityBuilder, EntityBui pub use entity_ref::{ComponentRef, ComponentRefShared, EntityRef, Ref, RefMut}; pub use query::{ Access, Batch, BatchedIter, Or, PreparedQuery, PreparedQueryBorrow, PreparedQueryIter, - PreparedView, Query, QueryBorrow, QueryIter, QueryMut, QueryShared, Satisfies, View, With, - Without, + PreparedView, Query, QueryBorrow, QueryIter, QueryMut, QueryShared, Satisfies, View, + ViewBorrowed, ViewMut, With, Without, }; pub use query_one::QueryOne; pub use take::TakenEntity; diff --git a/src/query.rs b/src/query.rs index 1b470b8c..f5f37f2e 100644 --- a/src/query.rs +++ b/src/query.rs @@ -1347,7 +1347,7 @@ impl<'q, Q: Query> View<'q, Q> { /// /// `'q` must be sufficient to guarantee that `Q` cannot violate borrow safety, either with /// dynamic borrow checks or by representing exclusive access to the `World`. - unsafe fn new(meta: &'q [EntityMeta], archetypes: &'q [Archetype]) -> Self { + pub(crate) unsafe fn new(meta: &'q [EntityMeta], archetypes: &'q [Archetype]) -> Self { let fetch = archetypes .iter() .map(|archetype| { @@ -1674,6 +1674,76 @@ pub(crate) fn assert_distinct(entities: &[Entity; N]) { } } } + +/// A borrow of a [`World`](crate::World) sufficient to execute the query `Q`, and immediately exposes a `View` +/// of it. +/// +/// Note that borrows are not released until this object is dropped. +/// +/// This struct is a thin wrapper around [`View`](crate::View). See it for more documentation. +pub struct ViewBorrowed<'w, Q: Query> { + _query: QueryBorrow<'w, Q>, + view: View<'w, Q>, +} + +impl<'w, Q: Query> ViewBorrowed<'w, Q> { + pub(crate) fn new(world: &'w World) -> Self { + let mut query = world.query::(); + query.borrow(); + let view = unsafe { View::::new(world.entities_meta(), world.archetypes_inner()) }; + + Self { + _query: query, + view, + } + } +} + +impl<'w, Q: Query> core::ops::Deref for ViewBorrowed<'w, Q> { + type Target = View<'w, Q>; + + fn deref(&self) -> &Self::Target { + &self.view + } +} + +impl<'w, Q: Query> core::ops::DerefMut for ViewBorrowed<'w, Q> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.view + } +} + +/// A borrow of a [`World`](crate::World) sufficient to execute the query `Q`, and immediately exposes a `View` +/// of it. +/// +/// This struct is a thin wrapper around [`View`](crate::View). See it for more documentation. +pub struct ViewMut<'w, Q: Query> { + view: View<'w, Q>, +} + +impl<'w, Q: Query> ViewMut<'w, Q> { + pub(crate) fn new(world: &'w mut World) -> Self { + assert_borrow::(); + let view = unsafe { View::::new(world.entities_meta(), world.archetypes_inner()) }; + + Self { view } + } +} + +impl<'w, Q: Query> core::ops::Deref for ViewMut<'w, Q> { + type Target = View<'w, Q>; + + fn deref(&self) -> &Self::Target { + &self.view + } +} + +impl<'w, Q: Query> core::ops::DerefMut for ViewMut<'w, Q> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.view + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/world.rs b/src/world.rs index 8118ab6a..9558bccf 100644 --- a/src/world.rs +++ b/src/world.rs @@ -25,7 +25,7 @@ use crate::entities::{Entities, EntityMeta, Location, ReserveEntitiesIterator}; use crate::query::{assert_borrow, assert_distinct}; use crate::{ Bundle, ColumnBatch, ComponentRef, DynamicBundle, Entity, EntityRef, Fetch, MissingComponent, - NoSuchEntity, Query, QueryBorrow, QueryMut, QueryOne, TakenEntity, + NoSuchEntity, Query, QueryBorrow, QueryMut, QueryOne, TakenEntity, ViewBorrowed, ViewMut, }; /// An unordered collection of entities, each having any number of distinctly typed components @@ -399,6 +399,16 @@ impl World { QueryBorrow::new(self) } + /// Provide random access to any entity for a given Query. + pub fn view(&self) -> ViewBorrowed<'_, Q> { + ViewBorrowed::new(self) + } + /// Provide random access to any entity for a given Query on a uniquely + /// borrowed world. Like [`view`](Self::view), but faster because dynamic borrow checks can be skipped. + pub fn view_mut(&mut self) -> ViewMut<'_, Q> { + ViewMut::new(self) + } + /// Query a uniquely borrowed world /// /// Like [`query`](Self::query), but faster because dynamic borrow checks can be skipped. Note @@ -1381,4 +1391,68 @@ mod tests { let mut world = World::new(); assert!(world.insert_one(Entity::DANGLING, ()).is_err()); } + + #[test] + fn view_borrowed() { + let mut world = World::new(); + let e0 = world.spawn((3, "hello")); + let e1 = world.spawn((6.0, "world")); + let e2 = world.spawn((12,)); + + { + let str_view = world.view::<&&str>(); + + assert_eq!(*str_view.get(e0).unwrap(), "hello"); + assert_eq!(*str_view.get(e1).unwrap(), "world"); + assert_eq!(str_view.get(e2), None); + } + + { + let mut int_view = world.view::<&mut i32>(); + assert_eq!(*int_view.get_mut(e0).unwrap(), 3); + assert_eq!(int_view.get_mut(e1), None); + assert_eq!(*int_view.get_mut(e2).unwrap(), 12); + + // edit some value + *int_view.get_mut(e0).unwrap() = 100; + } + + { + let mut int_str_view = world.view::<(&&str, &mut i32)>(); + let (s, i) = int_str_view.get_mut(e0).unwrap(); + assert_eq!(*s, "hello"); + assert_eq!(*i, 100); + assert_eq!(int_str_view.get_mut(e1), None); + assert_eq!(int_str_view.get_mut(e2), None); + } + } + + #[test] + fn view_mut() { + let mut world = World::new(); + let e0 = world.spawn((3, "hello")); + let e1 = world.spawn((6.0, "world")); + let e2 = world.spawn((12,)); + + let str_view = world.view_mut::<&&str>(); + + assert_eq!(*str_view.get(e0).unwrap(), "hello"); + assert_eq!(*str_view.get(e1).unwrap(), "world"); + assert_eq!(str_view.get(e2), None); + + let mut int_view = world.view_mut::<&mut i32>(); + assert_eq!(*int_view.get_mut(e0).unwrap(), 3); + assert_eq!(int_view.get_mut(e1), None); + assert_eq!(*int_view.get_mut(e2).unwrap(), 12); + + // edit some value + *int_view.get_mut(e0).unwrap() = 100; + + let mut int_str_view = world.view_mut::<(&&str, &mut i32)>(); + let (s, i) = int_str_view.get_mut(e0).unwrap(); + assert_eq!(*s, "hello"); + assert_eq!(*i, 100); + assert_eq!(int_str_view.get_mut(e1), None); + assert_eq!(int_str_view.get_mut(e2), None); + } } From 55c84c44c54a22629122f280033fc71bde4f4251 Mon Sep 17 00:00:00 2001 From: Jonathan Spira Date: Thu, 21 Mar 2024 22:56:11 -0400 Subject: [PATCH 2/6] even less code --- src/lib.rs | 2 +- src/query.rs | 31 ------------------------------- src/world.rs | 7 ++++--- 3 files changed, 5 insertions(+), 35 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c2b660b0..ce7fe2d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -98,7 +98,7 @@ pub use entity_ref::{ComponentRef, ComponentRefShared, EntityRef, Ref, RefMut}; pub use query::{ Access, Batch, BatchedIter, Or, PreparedQuery, PreparedQueryBorrow, PreparedQueryIter, PreparedView, Query, QueryBorrow, QueryIter, QueryMut, QueryShared, Satisfies, View, - ViewBorrowed, ViewMut, With, Without, + ViewBorrowed, With, Without, }; pub use query_one::QueryOne; pub use take::TakenEntity; diff --git a/src/query.rs b/src/query.rs index f5f37f2e..fd941d64 100644 --- a/src/query.rs +++ b/src/query.rs @@ -1713,37 +1713,6 @@ impl<'w, Q: Query> core::ops::DerefMut for ViewBorrowed<'w, Q> { } } -/// A borrow of a [`World`](crate::World) sufficient to execute the query `Q`, and immediately exposes a `View` -/// of it. -/// -/// This struct is a thin wrapper around [`View`](crate::View). See it for more documentation. -pub struct ViewMut<'w, Q: Query> { - view: View<'w, Q>, -} - -impl<'w, Q: Query> ViewMut<'w, Q> { - pub(crate) fn new(world: &'w mut World) -> Self { - assert_borrow::(); - let view = unsafe { View::::new(world.entities_meta(), world.archetypes_inner()) }; - - Self { view } - } -} - -impl<'w, Q: Query> core::ops::Deref for ViewMut<'w, Q> { - type Target = View<'w, Q>; - - fn deref(&self) -> &Self::Target { - &self.view - } -} - -impl<'w, Q: Query> core::ops::DerefMut for ViewMut<'w, Q> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.view - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/world.rs b/src/world.rs index 9558bccf..81be8642 100644 --- a/src/world.rs +++ b/src/world.rs @@ -25,7 +25,7 @@ use crate::entities::{Entities, EntityMeta, Location, ReserveEntitiesIterator}; use crate::query::{assert_borrow, assert_distinct}; use crate::{ Bundle, ColumnBatch, ComponentRef, DynamicBundle, Entity, EntityRef, Fetch, MissingComponent, - NoSuchEntity, Query, QueryBorrow, QueryMut, QueryOne, TakenEntity, ViewBorrowed, ViewMut, + NoSuchEntity, Query, QueryBorrow, QueryMut, QueryOne, TakenEntity, View, ViewBorrowed, }; /// An unordered collection of entities, each having any number of distinctly typed components @@ -405,8 +405,9 @@ impl World { } /// Provide random access to any entity for a given Query on a uniquely /// borrowed world. Like [`view`](Self::view), but faster because dynamic borrow checks can be skipped. - pub fn view_mut(&mut self) -> ViewMut<'_, Q> { - ViewMut::new(self) + pub fn view_mut(&mut self) -> View<'_, Q> { + assert_borrow::(); + unsafe { View::::new(self.entities_meta(), self.archetypes_inner()) } } /// Query a uniquely borrowed world From ad3ac8d8b5c000d3ed6d091f11eed2a1138ed3b1 Mon Sep 17 00:00:00 2001 From: Jonathan Spira Date: Mon, 25 Mar 2024 21:44:42 -0400 Subject: [PATCH 3/6] next step in review: removed deref to View, and manually handled boilerplate --- src/lib.rs | 2 +- src/query.rs | 171 +++++++++++++++++++++++++++++++++++-------------- src/world.rs | 71 ++------------------ tests/tests.rs | 63 ++++++++++++++++++ 4 files changed, 191 insertions(+), 116 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ce7fe2d9..dd077861 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -98,7 +98,7 @@ pub use entity_ref::{ComponentRef, ComponentRefShared, EntityRef, Ref, RefMut}; pub use query::{ Access, Batch, BatchedIter, Or, PreparedQuery, PreparedQueryBorrow, PreparedQueryIter, PreparedView, Query, QueryBorrow, QueryIter, QueryMut, QueryShared, Satisfies, View, - ViewBorrowed, With, Without, + ViewBorrow, With, Without, }; pub use query_one::QueryOne; pub use take::TakenEntity; diff --git a/src/query.rs b/src/query.rs index fd941d64..1576272c 100644 --- a/src/query.rs +++ b/src/query.rs @@ -649,15 +649,7 @@ impl<'w, Q: Query> QueryBorrow<'w, Q> { if self.borrowed { return; } - for x in self.world.archetypes() { - if x.is_empty() { - continue; - } - // TODO: Release prior borrows on failure? - if let Some(state) = Q::Fetch::prepare(x) { - Q::Fetch::borrow(x, state); - } - } + start_borrow::(self.world.archetypes_inner()); self.borrowed = true; } @@ -728,14 +720,7 @@ unsafe impl<'w, Q: Query> Sync for QueryBorrow<'w, Q> where for<'a> Q::Item<'a>: impl<'w, Q: Query> Drop for QueryBorrow<'w, Q> { fn drop(&mut self) { if self.borrowed { - for x in self.world.archetypes() { - if x.is_empty() { - continue; - } - if let Some(state) = Q::Fetch::prepare(x) { - Q::Fetch::release(x, state); - } - } + release_borrow::(self.world.archetypes_inner()); } } } @@ -1656,6 +1641,109 @@ impl<'a, 'q, Q: Query> IntoIterator for &'a mut PreparedView<'q, Q> { } } +/// A borrow of a [`World`](crate::World) sufficient to random-access the results of the query `Q`. +/// +/// Note that borrows are not released until this object is dropped. +/// +/// This struct is a thin wrapper around [`View`](crate::View). See it for more documentation. +pub struct ViewBorrow<'w, Q: Query> { + view: View<'w, Q>, +} + +impl<'w, Q: Query> ViewBorrow<'w, Q> { + pub(crate) fn new(world: &'w World) -> Self { + start_borrow::(world.archetypes_inner()); + let view = unsafe { View::::new(world.entities_meta(), world.archetypes_inner()) }; + + Self { view } + } + + /// Retrieve the query results corresponding to `entity` + /// + /// Will yield `None` if the entity does not exist or does not match the query. + /// + /// Does not require exclusive access to the map, but is defined only for queries yielding only shared references. + pub fn get(&self, entity: Entity) -> Option> + where + Q: QueryShared, + { + self.view.get(entity) + } + + /// Retrieve the query results corresponding to `entity` + /// + /// Will yield `None` if the entity does not exist or does not match the query. + pub fn get_mut(&mut self, entity: Entity) -> Option> { + self.view.get_mut(entity) + } + + /// Equivalent to `get(entity).is_some()`, but does not require `Q: QueryShared` + pub fn contains(&self, entity: Entity) -> bool { + self.view.contains(entity) + } + + /// Like `get_mut`, but allows simultaneous access to multiple entities + /// + /// # Safety + /// + /// Must not be invoked while any unique borrow of the fetched components of `entity` is live. + pub unsafe fn get_unchecked(&self, entity: Entity) -> Option> { + self.view.get_unchecked(entity) + } + + /// Like `get_mut`, but allows checked simultaneous access to multiple entities + /// + /// For N > 3, the check for distinct entities will clone the array and take O(N log N) time. + /// + /// # Examples + /// + /// ``` + /// # use hecs::World; + /// let mut world = World::new(); + /// + /// let a = world.spawn((1, 1.0)); + /// let b = world.spawn((2, 4.0)); + /// let c = world.spawn((3, 9.0)); + /// + /// let mut query = world.query_mut::<&mut i32>(); + /// let mut view = query.view(); + /// let [a,b,c] = view.get_mut_n([a, b, c]); + /// + /// assert_eq!(*a.unwrap(), 1); + /// assert_eq!(*b.unwrap(), 2); + /// assert_eq!(*c.unwrap(), 3); + /// ``` + pub fn get_many_mut( + &mut self, + entities: [Entity; N], + ) -> [Option>; N] { + self.view.get_many_mut(entities) + } + + /// Iterate over all entities satisfying `Q` + /// + /// Equivalent to [`QueryBorrow::iter`]. + pub fn iter_mut(&mut self) -> ViewIter<'_, Q> { + self.view.iter_mut() + } +} + +impl<'w, Q: Query> Drop for ViewBorrow<'w, Q> { + fn drop(&mut self) { + release_borrow::(self.view.archetypes) + } +} + +impl<'a, 'q, Q: Query> IntoIterator for &'a mut ViewBorrow<'q, Q> { + type IntoIter = ViewIter<'a, Q>; + type Item = (Entity, Q::Item<'a>); + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.iter_mut() + } +} + pub(crate) fn assert_distinct(entities: &[Entity; N]) { match N { 1 => (), @@ -1675,41 +1763,28 @@ pub(crate) fn assert_distinct(entities: &[Entity; N]) { } } -/// A borrow of a [`World`](crate::World) sufficient to execute the query `Q`, and immediately exposes a `View` -/// of it. -/// -/// Note that borrows are not released until this object is dropped. -/// -/// This struct is a thin wrapper around [`View`](crate::View). See it for more documentation. -pub struct ViewBorrowed<'w, Q: Query> { - _query: QueryBorrow<'w, Q>, - view: View<'w, Q>, -} - -impl<'w, Q: Query> ViewBorrowed<'w, Q> { - pub(crate) fn new(world: &'w World) -> Self { - let mut query = world.query::(); - query.borrow(); - let view = unsafe { View::::new(world.entities_meta(), world.archetypes_inner()) }; - - Self { - _query: query, - view, +/// Start the borrow +fn start_borrow(archetypes: &[Archetype]) { + for x in archetypes { + if x.is_empty() { + continue; + } + // TODO: Release prior borrows on failure? + if let Some(state) = Q::Fetch::prepare(x) { + Q::Fetch::borrow(x, state); } } } -impl<'w, Q: Query> core::ops::Deref for ViewBorrowed<'w, Q> { - type Target = View<'w, Q>; - - fn deref(&self) -> &Self::Target { - &self.view - } -} - -impl<'w, Q: Query> core::ops::DerefMut for ViewBorrowed<'w, Q> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.view +/// Releases the borrow +fn release_borrow(archetypes: &[Archetype]) { + for x in archetypes { + if x.is_empty() { + continue; + } + if let Some(state) = Q::Fetch::prepare(x) { + Q::Fetch::release(x, state); + } } } diff --git a/src/world.rs b/src/world.rs index 81be8642..d6e7bc67 100644 --- a/src/world.rs +++ b/src/world.rs @@ -25,7 +25,7 @@ use crate::entities::{Entities, EntityMeta, Location, ReserveEntitiesIterator}; use crate::query::{assert_borrow, assert_distinct}; use crate::{ Bundle, ColumnBatch, ComponentRef, DynamicBundle, Entity, EntityRef, Fetch, MissingComponent, - NoSuchEntity, Query, QueryBorrow, QueryMut, QueryOne, TakenEntity, View, ViewBorrowed, + NoSuchEntity, Query, QueryBorrow, QueryMut, QueryOne, TakenEntity, View, ViewBorrow, }; /// An unordered collection of entities, each having any number of distinctly typed components @@ -400,9 +400,10 @@ impl World { } /// Provide random access to any entity for a given Query. - pub fn view(&self) -> ViewBorrowed<'_, Q> { - ViewBorrowed::new(self) + pub fn view(&self) -> ViewBorrow<'_, Q> { + ViewBorrow::new(self) } + /// Provide random access to any entity for a given Query on a uniquely /// borrowed world. Like [`view`](Self::view), but faster because dynamic borrow checks can be skipped. pub fn view_mut(&mut self) -> View<'_, Q> { @@ -1392,68 +1393,4 @@ mod tests { let mut world = World::new(); assert!(world.insert_one(Entity::DANGLING, ()).is_err()); } - - #[test] - fn view_borrowed() { - let mut world = World::new(); - let e0 = world.spawn((3, "hello")); - let e1 = world.spawn((6.0, "world")); - let e2 = world.spawn((12,)); - - { - let str_view = world.view::<&&str>(); - - assert_eq!(*str_view.get(e0).unwrap(), "hello"); - assert_eq!(*str_view.get(e1).unwrap(), "world"); - assert_eq!(str_view.get(e2), None); - } - - { - let mut int_view = world.view::<&mut i32>(); - assert_eq!(*int_view.get_mut(e0).unwrap(), 3); - assert_eq!(int_view.get_mut(e1), None); - assert_eq!(*int_view.get_mut(e2).unwrap(), 12); - - // edit some value - *int_view.get_mut(e0).unwrap() = 100; - } - - { - let mut int_str_view = world.view::<(&&str, &mut i32)>(); - let (s, i) = int_str_view.get_mut(e0).unwrap(); - assert_eq!(*s, "hello"); - assert_eq!(*i, 100); - assert_eq!(int_str_view.get_mut(e1), None); - assert_eq!(int_str_view.get_mut(e2), None); - } - } - - #[test] - fn view_mut() { - let mut world = World::new(); - let e0 = world.spawn((3, "hello")); - let e1 = world.spawn((6.0, "world")); - let e2 = world.spawn((12,)); - - let str_view = world.view_mut::<&&str>(); - - assert_eq!(*str_view.get(e0).unwrap(), "hello"); - assert_eq!(*str_view.get(e1).unwrap(), "world"); - assert_eq!(str_view.get(e2), None); - - let mut int_view = world.view_mut::<&mut i32>(); - assert_eq!(*int_view.get_mut(e0).unwrap(), 3); - assert_eq!(int_view.get_mut(e1), None); - assert_eq!(*int_view.get_mut(e2).unwrap(), 12); - - // edit some value - *int_view.get_mut(e0).unwrap() = 100; - - let mut int_str_view = world.view_mut::<(&&str, &mut i32)>(); - let (s, i) = int_str_view.get_mut(e0).unwrap(); - assert_eq!(*s, "hello"); - assert_eq!(*i, 100); - assert_eq!(int_str_view.get_mut(e1), None); - assert_eq!(int_str_view.get_mut(e2), None); - } } diff --git a/tests/tests.rs b/tests/tests.rs index bece118d..def90bfa 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -258,6 +258,69 @@ fn random_access_via_view_mut() { assert!(!view.contains(f)); } +#[test] +fn view_borrow_on_world() { + let mut world = World::new(); + let e0 = world.spawn((3, "hello")); + let e1 = world.spawn((6.0, "world")); + let e2 = world.spawn((12,)); + + { + let str_view = world.view::<&&str>(); + + assert_eq!(*str_view.get(e0).unwrap(), "hello"); + assert_eq!(*str_view.get(e1).unwrap(), "world"); + assert_eq!(str_view.get(e2), None); + } + + { + let mut int_view = world.view::<&mut i32>(); + assert_eq!(*int_view.get_mut(e0).unwrap(), 3); + assert_eq!(int_view.get_mut(e1), None); + assert_eq!(*int_view.get_mut(e2).unwrap(), 12); + + // edit some value + *int_view.get_mut(e0).unwrap() = 100; + } + + { + let mut int_str_view = world.view::<(&&str, &mut i32)>(); + let (s, i) = int_str_view.get_mut(e0).unwrap(); + assert_eq!(*s, "hello"); + assert_eq!(*i, 100); + assert_eq!(int_str_view.get_mut(e1), None); + assert_eq!(int_str_view.get_mut(e2), None); + } +} + +#[test] +fn view_mut_on_world() { + let mut world = World::new(); + let e0 = world.spawn((3, "hello")); + let e1 = world.spawn((6.0, "world")); + let e2 = world.spawn((12,)); + + let str_view = world.view_mut::<&&str>(); + assert_eq!(*str_view.get(e0).unwrap(), "hello"); + assert_eq!(*str_view.get(e1).unwrap(), "world"); + assert_eq!(str_view.get(e2), None); + + let mut int_view = world.view_mut::<&mut i32>(); + assert_eq!(*int_view.get_mut(e0).unwrap(), 3); + assert_eq!(int_view.get_mut(e1), None); + assert_eq!(*int_view.get_mut(e2).unwrap(), 12); + + // edit some value + *int_view.get_mut(e0).unwrap() = 100; + + let mut int_str_view = world.view_mut::<(&&str, &mut i32)>(); + let (s, i) = int_str_view.get_mut(e0).unwrap(); + assert_eq!(*s, "hello"); + assert_eq!(*i, 100); + assert_eq!(int_str_view.get_mut(e1), None); + assert_eq!(int_str_view.get_mut(e2), None); +} + #[test] #[should_panic] fn simultaneous_access_must_be_non_overlapping() { From 991d9f51c880cbb17d2e35d19681eacb1d979ef8 Mon Sep 17 00:00:00 2001 From: Jonathan Spira Date: Tue, 26 Mar 2024 09:02:28 -0400 Subject: [PATCH 4/6] updated docs --- src/query.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/query.rs b/src/query.rs index 1576272c..8d12f8ae 100644 --- a/src/query.rs +++ b/src/query.rs @@ -1663,6 +1663,8 @@ impl<'w, Q: Query> ViewBorrow<'w, Q> { /// Will yield `None` if the entity does not exist or does not match the query. /// /// Does not require exclusive access to the map, but is defined only for queries yielding only shared references. + /// + /// See [View::get]. pub fn get(&self, entity: Entity) -> Option> where Q: QueryShared, @@ -1673,17 +1675,23 @@ impl<'w, Q: Query> ViewBorrow<'w, Q> { /// Retrieve the query results corresponding to `entity` /// /// Will yield `None` if the entity does not exist or does not match the query. + /// + /// See [View::get_mut]. pub fn get_mut(&mut self, entity: Entity) -> Option> { self.view.get_mut(entity) } /// Equivalent to `get(entity).is_some()`, but does not require `Q: QueryShared` + /// + /// See [View::contains]. pub fn contains(&self, entity: Entity) -> bool { self.view.contains(entity) } /// Like `get_mut`, but allows simultaneous access to multiple entities /// + /// See [View::get_unchecked]. + /// /// # Safety /// /// Must not be invoked while any unique borrow of the fetched components of `entity` is live. @@ -1695,6 +1703,8 @@ impl<'w, Q: Query> ViewBorrow<'w, Q> { /// /// For N > 3, the check for distinct entities will clone the array and take O(N log N) time. /// + /// See [View::get_many_mut]. + /// /// # Examples /// /// ``` @@ -1705,9 +1715,8 @@ impl<'w, Q: Query> ViewBorrow<'w, Q> { /// let b = world.spawn((2, 4.0)); /// let c = world.spawn((3, 9.0)); /// - /// let mut query = world.query_mut::<&mut i32>(); - /// let mut view = query.view(); - /// let [a,b,c] = view.get_mut_n([a, b, c]); + /// let mut view = world.view_mut::<&mut i32>(); + /// let [a, b, c] = view.get_mut_n([a, b, c]); /// /// assert_eq!(*a.unwrap(), 1); /// assert_eq!(*b.unwrap(), 2); @@ -1722,7 +1731,7 @@ impl<'w, Q: Query> ViewBorrow<'w, Q> { /// Iterate over all entities satisfying `Q` /// - /// Equivalent to [`QueryBorrow::iter`]. + /// See [View::iter_mut] pub fn iter_mut(&mut self) -> ViewIter<'_, Q> { self.view.iter_mut() } From 26148201c67e154d9c4d3937aa0db7bbf695c5f8 Mon Sep 17 00:00:00 2001 From: Jonathan Spira Date: Tue, 26 Mar 2024 09:05:43 -0400 Subject: [PATCH 5/6] added some overlapping views checks --- tests/tests.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/tests.rs b/tests/tests.rs index def90bfa..9183a746 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -321,6 +321,20 @@ fn view_mut_on_world() { assert_eq!(int_str_view.get_mut(e2), None); } +#[should_panic] +#[test] +fn view_mut_panic() { + let mut world = World::new(); + let e = world.spawn(('a',)); + + // we should panic since we have two overlapping views: + let mut first_view = world.view::<&mut char>(); + let mut second_view = world.view::<&mut char>(); + + first_view.get_mut(e).unwrap(); + second_view.get_mut(e).unwrap(); +} + #[test] #[should_panic] fn simultaneous_access_must_be_non_overlapping() { From 381d13c9a723a284631069dd36935868934433e5 Mon Sep 17 00:00:00 2001 From: Jonathan Spira Date: Tue, 26 Mar 2024 17:31:46 -0400 Subject: [PATCH 6/6] doc comment update --- src/query.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/query.rs b/src/query.rs index 8d12f8ae..2f7d93f1 100644 --- a/src/query.rs +++ b/src/query.rs @@ -1645,7 +1645,7 @@ impl<'a, 'q, Q: Query> IntoIterator for &'a mut PreparedView<'q, Q> { /// /// Note that borrows are not released until this object is dropped. /// -/// This struct is a thin wrapper around [`View`](crate::View). See it for more documentation. +/// This struct is a thin wrapper around [`View`]. See it for more documentation. pub struct ViewBorrow<'w, Q: Query> { view: View<'w, Q>, } @@ -1664,7 +1664,7 @@ impl<'w, Q: Query> ViewBorrow<'w, Q> { /// /// Does not require exclusive access to the map, but is defined only for queries yielding only shared references. /// - /// See [View::get]. + /// See [`View::get``]. pub fn get(&self, entity: Entity) -> Option> where Q: QueryShared, @@ -1676,21 +1676,21 @@ impl<'w, Q: Query> ViewBorrow<'w, Q> { /// /// Will yield `None` if the entity does not exist or does not match the query. /// - /// See [View::get_mut]. + /// See [`View::get_mut``]. pub fn get_mut(&mut self, entity: Entity) -> Option> { self.view.get_mut(entity) } /// Equivalent to `get(entity).is_some()`, but does not require `Q: QueryShared` /// - /// See [View::contains]. + /// See [`View::contains``]. pub fn contains(&self, entity: Entity) -> bool { self.view.contains(entity) } /// Like `get_mut`, but allows simultaneous access to multiple entities /// - /// See [View::get_unchecked]. + /// See [`View::get_unchecked``]. /// /// # Safety /// @@ -1703,7 +1703,7 @@ impl<'w, Q: Query> ViewBorrow<'w, Q> { /// /// For N > 3, the check for distinct entities will clone the array and take O(N log N) time. /// - /// See [View::get_many_mut]. + /// See [`View::get_many_mut``]. /// /// # Examples /// @@ -1731,7 +1731,7 @@ impl<'w, Q: Query> ViewBorrow<'w, Q> { /// Iterate over all entities satisfying `Q` /// - /// See [View::iter_mut] + /// See [`View::iter_mut``] pub fn iter_mut(&mut self) -> ViewIter<'_, Q> { self.view.iter_mut() }