diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs
index cfe4557d64..85ff83c0aa 100644
--- a/crates/bevy_ecs/src/lib.rs
+++ b/crates/bevy_ecs/src/lib.rs
@@ -1484,6 +1484,7 @@ mod tests {
#[test]
fn resource_scope() {
let mut world = World::default();
+ assert!(world.try_resource_scope::(|_, _| {}).is_none());
world.insert_resource(A(0));
world.resource_scope(|world: &mut World, mut value: Mut| {
value.0 += 1;
diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs
index 491c3648dd..5152e549a8 100644
--- a/crates/bevy_ecs/src/world/mod.rs
+++ b/crates/bevy_ecs/src/world/mod.rs
@@ -2874,21 +2874,34 @@ impl World {
/// });
/// assert_eq!(world.get_resource::().unwrap().0, 2);
/// ```
+ ///
+ /// See also [`try_resource_scope`](Self::try_resource_scope).
#[track_caller]
pub fn resource_scope(&mut self, f: impl FnOnce(&mut World, Mut) -> U) -> U {
+ self.try_resource_scope(f)
+ .unwrap_or_else(|| panic!("resource does not exist: {}", core::any::type_name::()))
+ }
+
+ /// Temporarily removes the requested resource from this [`World`] if it exists, runs custom user code,
+ /// then re-adds the resource before returning. Returns `None` if the resource does not exist in this [`World`].
+ ///
+ /// This enables safe simultaneous mutable access to both a resource and the rest of the [`World`].
+ /// For more complex access patterns, consider using [`SystemState`](crate::system::SystemState).
+ ///
+ /// See also [`resource_scope`](Self::resource_scope).
+ pub fn try_resource_scope(
+ &mut self,
+ f: impl FnOnce(&mut World, Mut) -> U,
+ ) -> Option {
let last_change_tick = self.last_change_tick();
let change_tick = self.change_tick();
- let component_id = self
- .components
- .get_resource_id(TypeId::of::())
- .unwrap_or_else(|| panic!("resource does not exist: {}", core::any::type_name::()));
+ let component_id = self.components.get_resource_id(TypeId::of::())?;
let (ptr, mut ticks, mut _caller) = self
.storages
.resources
.get_mut(component_id)
- .and_then(ResourceData::remove)
- .unwrap_or_else(|| panic!("resource does not exist: {}", core::any::type_name::()));
+ .and_then(ResourceData::remove)?;
// Read the value onto the stack to avoid potential mut aliasing.
// SAFETY: `ptr` was obtained from the TypeId of `R`.
let mut value = unsafe { ptr.read::() };
@@ -2912,27 +2925,18 @@ impl World {
OwningPtr::make(value, |ptr| {
// SAFETY: pointer is of type R
unsafe {
- self.storages
- .resources
- .get_mut(component_id)
- .map(|info| {
- info.insert_with_ticks(
- ptr,
- ticks,
- #[cfg(feature = "track_change_detection")]
- _caller,
- );
- })
- .unwrap_or_else(|| {
- panic!(
- "No resource of type {} exists in the World.",
- core::any::type_name::()
- )
- });
+ self.storages.resources.get_mut(component_id).map(|info| {
+ info.insert_with_ticks(
+ ptr,
+ ticks,
+ #[cfg(feature = "track_change_detection")]
+ _caller,
+ );
+ })
}
- });
+ })?;
- result
+ Some(result)
}
/// Sends an [`Event`].