Skip to content

Commit 637a149

Browse files
committed
unsafeify World::entities_mut (#4093)
# Objective make bevy ecs a lil bit less unsound ## Solution make unsound API unsafe so that there is an unsafe block to blame: ```rust use bevy_ecs::prelude::*; #[derive(Debug, Component)] struct Foo(u8); fn main() { let mut world = World::new(); let e1 = world.spawn().id(); let e2 = world.spawn().insert(Foo(2)).id(); world.entities_mut().meta[0] = world.entities_mut().meta[1].clone(); let foo = world.entity(e1).get::<Foo>().unwrap(); // whoo i love having components i dont have dbg!(foo); } ``` This is not _strictly_ speaking UB, however: - `Query::get_multiple` cannot work if this is allowed - bevy_ecs is a pile of unsafe code whose soundness generally depends on the world being in a "correct" state with "no funny business" so it seems best to disallow this - it is trivial to get bevy to panic inside of functions with safety invariants that have been violated (the entity location is not valid) - it seems to violate what the safety invariant on `Entities::flush` is trying to ensure
1 parent 2b35dba commit 637a149

File tree

3 files changed

+14
-4
lines changed

3 files changed

+14
-4
lines changed

crates/bevy_ecs/src/entity/mod.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ impl<'a> core::iter::ExactSizeIterator for ReserveEntitiesIterator<'a> {}
199199

200200
#[derive(Debug, Default)]
201201
pub struct Entities {
202-
pub meta: Vec<EntityMeta>,
202+
pub(crate) meta: Vec<EntityMeta>,
203203

204204
/// The `pending` and `free_cursor` fields describe three sets of Entity IDs
205205
/// that have been freed or are in the process of being allocated:
@@ -544,6 +544,12 @@ impl Entities {
544544
}
545545
}
546546

547+
/// Accessor for getting the length of the vec in `self.meta`
548+
#[inline]
549+
pub fn meta_len(&self) -> usize {
550+
self.meta.len()
551+
}
552+
547553
#[inline]
548554
pub fn len(&self) -> u32 {
549555
self.len

crates/bevy_ecs/src/world/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,12 @@ impl World {
135135
}
136136

137137
/// Retrieves this world's [Entities] collection mutably
138+
///
139+
/// # Safety
140+
/// Mutable reference must not be used to put the [`Entities`] data
141+
/// in an invalid state for this [`World`]
138142
#[inline]
139-
pub fn entities_mut(&mut self) -> &mut Entities {
143+
pub unsafe fn entities_mut(&mut self) -> &mut Entities {
140144
&mut self.entities
141145
}
142146

crates/bevy_render/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ impl Plugin for RenderPlugin {
189189

190190
// reserve all existing app entities for use in render_app
191191
// they can only be spawned using `get_or_spawn()`
192-
let meta_len = app_world.entities().meta.len();
192+
let meta_len = app_world.entities().meta_len();
193193
render_app
194194
.world
195195
.entities()
@@ -198,7 +198,7 @@ impl Plugin for RenderPlugin {
198198
// flushing as "invalid" ensures that app world entities aren't added as "empty archetype" entities by default
199199
// these entities cannot be accessed without spawning directly onto them
200200
// this _only_ works as expected because clear_entities() is called at the end of every frame.
201-
render_app.world.entities_mut().flush_as_invalid();
201+
unsafe { render_app.world.entities_mut() }.flush_as_invalid();
202202
}
203203

204204
{

0 commit comments

Comments
 (0)