diff --git a/crates/bevy_ecs/src/query/mod.rs b/crates/bevy_ecs/src/query/mod.rs index 5e5574381ca54..ad04730918f6f 100644 --- a/crates/bevy_ecs/src/query/mod.rs +++ b/crates/bevy_ecs/src/query/mod.rs @@ -438,6 +438,18 @@ mod tests { ); } + #[test] + fn get_many_only_mut_checks_duplicates() { + let mut world = World::new(); + let id = world.spawn(A(10)).id(); + let mut query_state = world.query::<&mut A>(); + let mut query = query_state.query_mut(&mut world); + let result = query.get_many([id, id]); + assert_eq!(result, Ok([&A(10), &A(10)])); + let mut_result = query.get_many_mut([id, id]); + assert!(mut_result.is_err()); + } + #[test] fn multi_storage_query() { let mut world = World::new(); diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index ecca479d3bf9c..fe5662febc77f 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -1135,7 +1135,9 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { &self, entities: [Entity; N], ) -> Result<[ROQueryItem<'_, D>; N], QueryEntityError> { - self.as_readonly().get_many_inner(entities) + // Note that this calls `get_many_readonly` instead of `get_many_inner` + // since we don't need to check for duplicates. + self.as_readonly().get_many_readonly(entities) } /// Returns the read-only query items for the given array of [`Entity`]. @@ -1249,7 +1251,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// /// # See also /// - /// - [`get_many`](Self::get_many) to get read-only query items. + /// - [`get_many`](Self::get_many) to get read-only query items without checking for duplicate entities. /// - [`many_mut`](Self::many_mut) for the panicking version. #[inline] pub fn get_many_mut( @@ -1267,8 +1269,10 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// /// # See also /// - /// - [`get_many`](Self::get_many) to get read-only query items. + /// - [`get_many`](Self::get_many) to get read-only query items without checking for duplicate entities. /// - [`get_many_mut`](Self::get_many_mut) to get items using a mutable reference. + /// - [`get_many_readonly`](Self::get_many_readonly) to get read-only query items without checking for duplicate entities + /// with the actual "inner" world lifetime. #[inline] pub fn get_many_inner( self, @@ -1281,6 +1285,32 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { } } + /// Returns the query items for the given array of [`Entity`]. + /// This consumes the [`Query`] to return results with the actual "inner" world lifetime. + /// + /// The returned query items are in the same order as the input. + /// In case of a nonexisting entity or mismatched component, a [`QueryEntityError`] is returned instead. + /// + /// # See also + /// + /// - [`get_many`](Self::get_many) to get read-only query items without checking for duplicate entities. + /// - [`get_many_mut`](Self::get_many_mut) to get items using a mutable reference. + /// - [`get_many_inner`](Self::get_many_readonly) to get mutable query items with the actual "inner" world lifetime. + #[inline] + pub fn get_many_readonly( + self, + entities: [Entity; N], + ) -> Result<[D::Item<'w>; N], QueryEntityError<'w>> + where + D: ReadOnlyQueryData, + { + // SAFETY: scheduler ensures safe Query world access + unsafe { + self.state + .get_many_read_only_manual(self.world, entities, self.last_run, self.this_run) + } + } + /// Returns the query items for the given array of [`Entity`]. /// /// # Panics