use UnsafeWorldCell internally#6972
Conversation
|
@jakobhellermann can you rebase this? I'd like to prioritize this for review and merging due to its impact on ECS internals clarity. |
5ce7cbe to
7cc5b86
Compare
7cc5b86 to
ebc7198
Compare
8abb3cf to
93dbca5
Compare
InteriorMutableWorld internallyUnsafeWorldCell internally
93dbca5 to
f7606e9
Compare
|
I'm unsure if we can get this reviewed and merged appropriately without stopping the train. Impact is mostly internal, so I've bumped it from the 0.10 milestone. |
| } | ||
| } | ||
|
|
||
| fn update_archetypes_unsafe_world_cell(&mut self, world: UnsafeWorldCell<'_>) { |
There was a problem hiding this comment.
| fn update_archetypes_unsafe_world_cell(&mut self, world: UnsafeWorldCell<'_>) { | |
| pub fn update_archetypes_unsafe_world_cell(&mut self, world: UnsafeWorldCell<'_>) { |
and also implement update_archetypes in terms of this method rather than the way it is now perhaps
| self.validate_world_unsafe_world_cell(world.as_unsafe_world_cell_readonly()) | ||
| } | ||
| #[inline] | ||
| pub fn validate_world_unsafe_world_cell(&self, world: UnsafeWorldCell<'_>) { |
There was a problem hiding this comment.
| pub fn validate_world_unsafe_world_cell(&self, world: UnsafeWorldCell<'_>) { | |
| pub fn validate_unsafe_world_cell(&self, world: UnsafeWorldCell<'_>) { |
| break; | ||
| } | ||
|
|
||
| // TODO: do this earlier? |
There was a problem hiding this comment.
Yes, you want to replace the cell: &'scope SyncUnsafeCell<World> with cell: UnsafeWorldCell<'scope> I believe.
| .sparse_sets | ||
| .get(component_id) | ||
| .debug_checked_unwrap() | ||
| // SAFETY: TODO |
There was a problem hiding this comment.
This needs to be a non-TODO comment.
| .sparse_sets | ||
| .get(component_id) | ||
| .debug_checked_unwrap() | ||
| // SAFETY: world has access to |
There was a problem hiding this comment.
This placeholder needs to be fully written.
| #[inline] | ||
| pub fn validate_world(&self, world: &World) { | ||
| self.validate_world_unsafe_world_cell(world.as_unsafe_world_cell_readonly()) | ||
| } |
| self.validate_world_unsafe_world_cell(world.as_unsafe_world_cell_readonly()) | ||
| } | ||
| #[inline] | ||
| pub fn validate_world_unsafe_world_cell(&self, world: UnsafeWorldCell<'_>) { |
There was a problem hiding this comment.
If this is to be public, please document what this function does. Alternatively, we could just have validate_world take a WorldId instead, which should be accessible from both.
|
Closing in favor of #8833. |
# Objective Follow-up to #6404 and #8292. Mutating the world through a shared reference is surprising, and it makes the meaning of `&World` unclear: sometimes it gives read-only access to the entire world, and sometimes it gives interior mutable access to only part of it. This is an up-to-date version of #6972. ## Solution Use `UnsafeWorldCell` for all interior mutability. Now, `&World` *always* gives you read-only access to the entire world. --- ## Changelog TODO - do we still care about changelogs? ## Migration Guide Mutating any world data using `&World` is now considered unsound -- the type `UnsafeWorldCell` must be used to achieve interior mutability. The following methods now accept `UnsafeWorldCell` instead of `&World`: - `QueryState`: `get_unchecked`, `iter_unchecked`, `iter_combinations_unchecked`, `for_each_unchecked`, `get_single_unchecked`, `get_single_unchecked_manual`. - `SystemState`: `get_unchecked_manual` ```rust let mut world = World::new(); let mut query = world.query::<&mut T>(); // Before: let t1 = query.get_unchecked(&world, entity_1); let t2 = query.get_unchecked(&world, entity_2); // After: let world_cell = world.as_unsafe_world_cell(); let t1 = query.get_unchecked(world_cell, entity_1); let t2 = query.get_unchecked(world_cell, entity_2); ``` The methods `QueryState::validate_world` and `SystemState::matches_world` now take a `WorldId` instead of `&World`: ```rust // Before: query_state.validate_world(&world); // After: query_state.validate_world(world.id()); ``` The methods `QueryState::update_archetypes` and `SystemState::update_archetypes` now take `UnsafeWorldCell` instead of `&World`: ```rust // Before: query_state.update_archetypes(&world); // After: query_state.update_archetypes(world.as_unsafe_world_cell_readonly()); ```
builds on #6404 (and #6402)
important commits:
SystemandParamStateQueryState/QueryObjective
UnsafeWorldCellabstraction #6404 and the issue Abstraction for working with a&Worldand interior mutability #5956, but the TLDR is:&Worldis sometimes used internally to mean "world that is shared with others but the access is distinct"get_resourcethat is unsound because the&Worlddoesn't actually give accessSolution
UnsafeWorldCellwhenever we use&Worldas shared mutableSystemmachinery and forQuery/WorldQueryChangelog
bevy now uses the
UnsafeWorldCelltype in places where we previously used&Worldto mean "shared mutable access, but ensured to be distinct by systems".This means that systems and queries now get a
UnsafeWorldCellwhere every method is unsafe, so you need to reason correctly about every access.Usually this is done like