mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 12:43:34 +00:00
3643074f21
# Objective Consider the test ```rust let cell = world.cell(); let _value_a = cell.resource_mut::<A>(); let _value_b = cell.resource_mut::<A>(); ``` Currently, this will roughly execute ```rust // first call let value = unsafe { self.world .get_non_send_unchecked_mut_with_id(component_id)? }; return Some(WorldBorrowMut::new(value, archetype_component_id, self.access))) // second call let value = unsafe { self.world .get_non_send_unchecked_mut_with_id(component_id)? }; return Some(WorldBorrowMut::new(value, archetype_component_id, self.access))) ``` where `WorldBorrowMut::new` will panic if the resource is already borrowed. This means, that `_value_a` will be created, the access checked (OK), then `value_b` will be created, and the access checked (`panic`). For a moment, both `_value_a` and `_value_b` existed as `&mut T` to the same location, which is insta-UB as far as I understand it. ## Solution Flip the order so that `WorldBorrowMut::new` first checks the access, _then_ fetches creates the value. To do that, we pass a `impl FnOnce() -> Mut<T>` instead of the `Mut<T>` directly: ```rust let get_value = || unsafe { self.world .get_non_send_unchecked_mut_with_id(component_id)? }; return Some(WorldBorrowMut::new(get_value, archetype_component_id, self.access))) ``` |
||
---|---|---|
.. | ||
bevy_animation | ||
bevy_app | ||
bevy_asset | ||
bevy_audio | ||
bevy_core | ||
bevy_core_pipeline | ||
bevy_derive | ||
bevy_diagnostic | ||
bevy_dylib | ||
bevy_dynamic_plugin | ||
bevy_ecs | ||
bevy_ecs_compile_fail_tests | ||
bevy_encase_derive | ||
bevy_gilrs | ||
bevy_gltf | ||
bevy_hierarchy | ||
bevy_input | ||
bevy_internal | ||
bevy_log | ||
bevy_macro_utils | ||
bevy_math | ||
bevy_mikktspace | ||
bevy_pbr | ||
bevy_ptr | ||
bevy_reflect | ||
bevy_render | ||
bevy_scene | ||
bevy_sprite | ||
bevy_tasks | ||
bevy_text | ||
bevy_time | ||
bevy_transform | ||
bevy_ui | ||
bevy_utils | ||
bevy_window | ||
bevy_winit |