From c0e7fc9a2fdb9f78009140a8eba04cab926b4c0a Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Sun, 16 Jan 2022 11:48:18 +0000 Subject: [PATCH 1/6] Make `Bundle`* object safe --- crates/bevy_ecs/src/bundle.rs | 107 ++++++++++++++++++--- crates/bevy_ecs/src/lib.rs | 2 +- crates/bevy_ecs/src/system/commands/mod.rs | 37 +++---- crates/bevy_ecs/src/world/entity_ref.rs | 14 +-- 4 files changed, 124 insertions(+), 36 deletions(-) diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index 3bf6881eb31d2..a491779f3d5f8 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -22,6 +22,8 @@ use std::{any::TypeId, collections::HashMap}; /// particularly useful for spawning multiple empty entities by using /// [`Commands::spawn_batch`](crate::system::Commands::spawn_batch). /// +/// To use a bundle dynamically, use [`Box`](`ApplicableBundle`) +/// /// # Examples /// /// Typically, you will simply use `#[derive(Bundle)]` when creating your own `Bundle`. Each @@ -75,19 +77,17 @@ use std::{any::TypeId, collections::HashMap}; /// bundle, in the _exact_ order that [`Bundle::get_components`] is called. /// - [`Bundle::from_components`] must call `func` exactly once for each [`ComponentId`] returned by /// [`Bundle::component_ids`]. -pub unsafe trait Bundle: Send + Sync + 'static { - /// Gets this [`Bundle`]'s component ids, in the order of this bundle's [`Component`]s - fn component_ids(components: &mut Components, storages: &mut Storages) -> Vec; - +pub unsafe trait Bundle: ApplicableBundle + Sized { /// Calls `func`, which should return data for each component in the bundle, in the order of /// this bundle's [`Component`]s /// /// # Safety /// Caller must return data for each component in the bundle, in the order of this bundle's /// [`Component`]s - unsafe fn from_components(func: impl FnMut() -> *mut u8) -> Self - where - Self: Sized; + unsafe fn from_components(func: impl FnMut() -> *mut u8) -> Self; + + /// Gets this [`Bundle`]'s component ids, in the order of this bundle's [`Component`]s + fn component_ids(components: &mut Components, storages: &mut Storages) -> Vec; /// Calls `func` on each value, in the order of this bundle's [`Component`]s. This will /// [`std::mem::forget`] the bundle fields, so callers are responsible for dropping the fields @@ -95,6 +95,89 @@ pub unsafe trait Bundle: Send + Sync + 'static { fn get_components(self, func: impl FnMut(*mut u8)); } +mod sealed { + use super::{ApplicableBundle, Bundle}; + + pub trait SealedApplicableBundle {} + impl SealedApplicableBundle for T where T: Bundle {} + impl SealedApplicableBundle for Box {} +} + +/// A bundle which can be applied to a world +/// +/// # Safety: +/// The bundle returned from `init_bundle_info` is the same as used for `get_component_box`. +/// +/// This trait is sealed and cannot be implemented outside of `bevy_ecs` +/// +/// However, it is implemented for every type which implements [`Bundle`] + +pub unsafe trait ApplicableBundle: + Send + Sync + 'static + sealed::SealedApplicableBundle +{ + fn init_bundle_info<'a>( + &self, + bundles: &'a mut Bundles, + components: &mut Components, + storages: &mut Storages, + ) -> &'a BundleInfo; + // Same requirements as [`Bundle::get_components`] + fn get_components_box(self: Box, func: &mut dyn FnMut(*mut u8)); + // Same requirements as [`Bundle::get_components`] + fn get_components_self(self, func: impl FnMut(*mut u8)) + where + Self: Sized; +} + +unsafe impl ApplicableBundle for T +where + T: Bundle, +{ + fn init_bundle_info<'a>( + &self, + bundles: &'a mut Bundles, + components: &mut Components, + storages: &mut Storages, + ) -> &'a BundleInfo { + bundles.init_info::(components, storages) + } + + fn get_components_box(self: Box, func: &mut dyn FnMut(*mut u8)) { + Self::get_components(*self, func) + } + + fn get_components_self(self, func: impl FnMut(*mut u8)) + where + Self: Sized, + { + Self::get_components(self, func) + } +} + +unsafe impl ApplicableBundle for Box { + fn init_bundle_info<'a>( + &self, + bundles: &'a mut Bundles, + components: &mut Components, + storages: &mut Storages, + ) -> &'a BundleInfo { + ::init_bundle_info( + self, bundles, components, storages, + ) + } + + fn get_components_box(self: Box, func: &mut dyn FnMut(*mut u8)) { + ::get_components_box(self, func) + } + + fn get_components_self(self, mut func: impl FnMut(*mut u8)) + where + Self: Sized, + { + ::get_components_box(self, &mut func) + } +} + macro_rules! tuple_impl { ($($name: ident),*) => { unsafe impl<$($name: Component),*> Bundle for ($($name,)*) { @@ -261,7 +344,7 @@ impl BundleInfo { /// `entity`, `bundle` must match this [`BundleInfo`]'s type #[inline] #[allow(clippy::too_many_arguments)] - unsafe fn write_components( + unsafe fn write_components( &self, table: &mut Table, sparse_sets: &mut SparseSets, @@ -274,7 +357,7 @@ impl BundleInfo { // NOTE: get_components calls this closure on each component in "bundle order". // bundle_info.component_ids are also in "bundle order" let mut bundle_component = 0; - bundle.get_components(|component_ptr| { + bundle.get_components_self(|component_ptr| { let component_id = *self.component_ids.get_unchecked(bundle_component); match self.storage_types[bundle_component] { StorageType::Table => { @@ -412,7 +495,7 @@ impl<'a, 'b> BundleInserter<'a, 'b> { /// `entity` must currently exist in the source archetype for this inserter. `archetype_index` /// must be `entity`'s location in the archetype. `T` must match this [`BundleInfo`]'s type #[inline] - pub unsafe fn insert( + pub unsafe fn insert( &mut self, entity: Entity, archetype_index: usize, @@ -540,7 +623,7 @@ impl<'a, 'b> BundleSpawner<'a, 'b> { /// # Safety /// `entity` must be allocated (but non-existent), `T` must match this [`BundleInfo`]'s type #[inline] - pub unsafe fn spawn_non_existent( + pub unsafe fn spawn_non_existent( &mut self, entity: Entity, bundle: T, @@ -564,7 +647,7 @@ impl<'a, 'b> BundleSpawner<'a, 'b> { /// # Safety /// `T` must match this [`BundleInfo`]'s type #[inline] - pub unsafe fn spawn(&mut self, bundle: T) -> Entity { + pub unsafe fn spawn(&mut self, bundle: T) -> Entity { let entity = self.entities.alloc(); // SAFE: entity is allocated (but non-existent), `T` matches this BundleInfo's type self.spawn_non_existent(entity, bundle); diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 30d70ca17efab..198b8c994291c 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -21,7 +21,7 @@ pub mod prelude { pub use crate::reflect::ReflectComponent; #[doc(hidden)] pub use crate::{ - bundle::Bundle, + bundle::{ApplicableBundle, Bundle}, change_detection::DetectChanges, component::Component, entity::Entity, diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 6de329856cf2a..d6fdec260afb5 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -1,7 +1,7 @@ mod command_queue; use crate::{ - bundle::Bundle, + bundle::{ApplicableBundle, Bundle}, component::Component, entity::{Entities, Entity}, world::World, @@ -50,7 +50,7 @@ impl<'w, 's> Commands<'w, 's> { /// Creates a new empty [`Entity`] and returns an [`EntityCommands`] builder for it. /// - /// To directly spawn an entity with a [`Bundle`] included, you can use + /// To directly spawn an entity with a [`ApplicableBundle`] included, you can use /// [`spawn_bundle`](Self::spawn_bundle) instead of `.spawn().insert_bundle()`. /// /// See [`World::spawn`] for more details. @@ -104,9 +104,9 @@ impl<'w, 's> Commands<'w, 's> { } } - /// Spawns a [`Bundle`] without pre-allocating an [`Entity`]. The [`Entity`] will be allocated + /// Spawns a [`ApplicableBundle`] without pre-allocating an [`Entity`]. The [`Entity`] will be allocated /// when this [`Command`] is applied. - pub fn spawn_and_forget(&mut self, bundle: impl Bundle) { + pub fn spawn_and_forget(&mut self, bundle: impl ApplicableBundle) { self.queue.push(Spawn { bundle }) } @@ -115,9 +115,9 @@ impl<'w, 's> Commands<'w, 's> { /// This returns an [`EntityCommands`] builder, which enables inserting more components and /// bundles using a "builder pattern". /// - /// Note that `bundle` is a [`Bundle`], which is a collection of components. [`Bundle`] is + /// Note that `bundle` is a [`ApplicableBundle`], which is a collection of components. [`ApplicableBundle`] is /// automatically implemented for tuples of components. You can also create your own bundle - /// types by deriving [`derive@Bundle`]. + /// types by deriving [`derive@ApplicableBundle`]. /// /// # Example /// @@ -135,7 +135,7 @@ impl<'w, 's> Commands<'w, 's> { /// #[derive(Component)] /// struct Agility(u32); /// - /// #[derive(Bundle)] + /// #[derive(ApplicableBundle)] /// struct ExampleBundle { /// a: Component1, /// b: Component2, @@ -158,7 +158,10 @@ impl<'w, 's> Commands<'w, 's> { /// } /// # example_system.system(); /// ``` - pub fn spawn_bundle<'a, T: Bundle>(&'a mut self, bundle: T) -> EntityCommands<'w, 's, 'a> { + pub fn spawn_bundle<'a, T: ApplicableBundle>( + &'a mut self, + bundle: T, + ) -> EntityCommands<'w, 's, 'a> { let mut e = self.spawn(); e.insert_bundle(bundle); e @@ -328,7 +331,7 @@ impl<'w, 's> Commands<'w, 's> { /// # #[derive(Component)] /// # struct Defense(u32); /// # - /// # #[derive(Bundle)] + /// # #[derive(ApplicableBundle)] /// # struct CombatBundle { /// # health: Health, /// # strength: Strength, @@ -376,7 +379,7 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> { self.entity } - /// Adds a [`Bundle`] of components to the entity. + /// Adds a [`ApplicableBundle`] of components to the entity. /// /// # Example /// @@ -391,7 +394,7 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> { /// # #[derive(Component)] /// # struct Defense(u32); /// # - /// # #[derive(Bundle)] + /// # #[derive(ApplicableBundle)] /// # struct CombatBundle { /// # health: Health, /// # strength: Strength, @@ -407,7 +410,7 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> { /// } /// # add_combat_stats_system.system(); /// ``` - pub fn insert_bundle(&mut self, bundle: impl Bundle) -> &mut Self { + pub fn insert_bundle(&mut self, bundle: impl ApplicableBundle) -> &mut Self { self.commands.add(InsertBundle { entity: self.entity, bundle, @@ -449,7 +452,7 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> { self } - /// Removes a [`Bundle`] of components from the entity. + /// Removes a [`ApplicableBundle`] of components from the entity. /// /// See [`EntityMut::remove_bundle`](crate::world::EntityMut::remove_bundle) for more /// details. @@ -463,7 +466,7 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> { /// # /// # #[derive(Component)] /// struct Dummy; - /// # #[derive(Bundle)] + /// # #[derive(ApplicableBundle)] /// # struct CombatBundle { a: Dummy }; // dummy field, unit bundles are not permitted. /// # /// fn remove_combat_stats_system(mut commands: Commands, player: Res) { @@ -550,7 +553,7 @@ pub struct Spawn { impl Command for Spawn where - T: Bundle, + T: ApplicableBundle, { fn write(self, world: &mut World) { world.spawn().insert_bundle(self.bundle); @@ -570,7 +573,7 @@ impl Command for GetOrSpawn { pub struct SpawnBatch where I: IntoIterator, - I::Item: Bundle, + I::Item: ApplicableBundle, { pub bundles_iter: I, } @@ -633,7 +636,7 @@ pub struct InsertBundle { impl Command for InsertBundle where - T: Bundle + 'static, + T: ApplicableBundle + 'static, { fn write(self, world: &mut World) { if let Some(mut entity) = world.get_entity_mut(self.entity) { diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index b85fa2b74f0f2..dd6ed3b9ac7d0 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -1,6 +1,6 @@ use crate::{ archetype::{Archetype, ArchetypeId, Archetypes}, - bundle::{Bundle, BundleInfo}, + bundle::{ApplicableBundle, Bundle, BundleInfo}, change_detection::Ticks, component::{Component, ComponentId, ComponentTicks, Components, StorageType}, entity::{Entities, Entity, EntityLocation}, @@ -189,12 +189,14 @@ impl<'w> EntityMut<'w> { }) } - pub fn insert_bundle(&mut self, bundle: T) -> &mut Self { + pub fn insert_bundle(&mut self, bundle: T) -> &mut Self { let change_tick = self.world.change_tick(); - let bundle_info = self - .world - .bundles - .init_info::(&mut self.world.components, &mut self.world.storages); + let bundle_info = bundle.init_bundle_info( + &mut self.world.bundles, + &mut self.world.components, + &mut self.world.storages, + ); + let mut bundle_inserter = bundle_info.get_bundle_inserter( &mut self.world.entities, &mut self.world.archetypes, From 4a13a80d8c70e0a65d263f342dfedcb898fc184b Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Sun, 16 Jan 2022 11:53:01 +0000 Subject: [PATCH 2/6] Clippy and the curse of the bonus colon --- crates/bevy_ecs/src/bundle.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index a491779f3d5f8..b0c58090b0da1 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -105,13 +105,12 @@ mod sealed { /// A bundle which can be applied to a world /// -/// # Safety: +/// # Safety /// The bundle returned from `init_bundle_info` is the same as used for `get_component_box`. /// /// This trait is sealed and cannot be implemented outside of `bevy_ecs` /// /// However, it is implemented for every type which implements [`Bundle`] - pub unsafe trait ApplicableBundle: Send + Sync + 'static + sealed::SealedApplicableBundle { From 0f2cbdd781c2d187c5cfa385b7873d3ebf5661ad Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Sun, 16 Jan 2022 12:05:21 +0000 Subject: [PATCH 3/6] Fix overzealous find and replace --- crates/bevy_ecs/src/system/commands/mod.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index d6fdec260afb5..404a408014958 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -50,7 +50,7 @@ impl<'w, 's> Commands<'w, 's> { /// Creates a new empty [`Entity`] and returns an [`EntityCommands`] builder for it. /// - /// To directly spawn an entity with a [`ApplicableBundle`] included, you can use + /// To directly spawn an entity with an [`ApplicableBundle`] included, you can use /// [`spawn_bundle`](Self::spawn_bundle) instead of `.spawn().insert_bundle()`. /// /// See [`World::spawn`] for more details. @@ -104,7 +104,7 @@ impl<'w, 's> Commands<'w, 's> { } } - /// Spawns a [`ApplicableBundle`] without pre-allocating an [`Entity`]. The [`Entity`] will be allocated + /// Spawns an [`ApplicableBundle`] without pre-allocating an [`Entity`]. The [`Entity`] will be allocated /// when this [`Command`] is applied. pub fn spawn_and_forget(&mut self, bundle: impl ApplicableBundle) { self.queue.push(Spawn { bundle }) @@ -115,9 +115,9 @@ impl<'w, 's> Commands<'w, 's> { /// This returns an [`EntityCommands`] builder, which enables inserting more components and /// bundles using a "builder pattern". /// - /// Note that `bundle` is a [`ApplicableBundle`], which is a collection of components. [`ApplicableBundle`] is + /// Note that `bundle` is an [`ApplicableBundle`], which is a collection of components. [`ApplicableBundle`] is /// automatically implemented for tuples of components. You can also create your own bundle - /// types by deriving [`derive@ApplicableBundle`]. + /// types by deriving [`derive@Bundle`]. /// /// # Example /// @@ -135,7 +135,7 @@ impl<'w, 's> Commands<'w, 's> { /// #[derive(Component)] /// struct Agility(u32); /// - /// #[derive(ApplicableBundle)] + /// #[derive(Bundle)] /// struct ExampleBundle { /// a: Component1, /// b: Component2, @@ -331,7 +331,7 @@ impl<'w, 's> Commands<'w, 's> { /// # #[derive(Component)] /// # struct Defense(u32); /// # - /// # #[derive(ApplicableBundle)] + /// # #[derive(Bundle)] /// # struct CombatBundle { /// # health: Health, /// # strength: Strength, @@ -379,7 +379,7 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> { self.entity } - /// Adds a [`ApplicableBundle`] of components to the entity. + /// Adds an [`ApplicableBundle`] of components to the entity. /// /// # Example /// @@ -394,7 +394,7 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> { /// # #[derive(Component)] /// # struct Defense(u32); /// # - /// # #[derive(ApplicableBundle)] + /// # #[derive(Bundle)] /// # struct CombatBundle { /// # health: Health, /// # strength: Strength, @@ -452,7 +452,7 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> { self } - /// Removes a [`ApplicableBundle`] of components from the entity. + /// Removes a [`Bundle`] of components from the entity. /// /// See [`EntityMut::remove_bundle`](crate::world::EntityMut::remove_bundle) for more /// details. @@ -466,7 +466,7 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> { /// # /// # #[derive(Component)] /// struct Dummy; - /// # #[derive(ApplicableBundle)] + /// # #[derive(Bundle)] /// # struct CombatBundle { a: Dummy }; // dummy field, unit bundles are not permitted. /// # /// fn remove_combat_stats_system(mut commands: Commands, player: Res) { From 57caadc215d449fc06c5371f22c03a009d261fe8 Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Sun, 16 Jan 2022 19:33:56 +0000 Subject: [PATCH 4/6] Apply suggestions from code review Co-authored-by: Alice Cecile --- crates/bevy_ecs/src/bundle.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index b0c58090b0da1..c9940ed20b061 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -103,10 +103,10 @@ mod sealed { impl SealedApplicableBundle for Box {} } -/// A bundle which can be applied to a world +/// A bundle that can be added to entities /// /// # Safety -/// The bundle returned from `init_bundle_info` is the same as used for `get_component_box`. +/// The bundle returned from `init_bundle_info` must be the same as used for `get_component_box`. /// /// This trait is sealed and cannot be implemented outside of `bevy_ecs` /// From 8cfd00926f6f3606a62bd4f21cdbe3ff32be3e9e Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Mon, 17 Jan 2022 19:40:37 +0000 Subject: [PATCH 5/6] Avoid infinite recursion --- crates/bevy_ecs/src/bundle.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index c9940ed20b061..2189f95e2faeb 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -160,13 +160,14 @@ unsafe impl ApplicableBundle for Box { components: &mut Components, storages: &mut Storages, ) -> &'a BundleInfo { + let this = &**self; ::init_bundle_info( - self, bundles, components, storages, + this, bundles, components, storages, ) } fn get_components_box(self: Box, func: &mut dyn FnMut(*mut u8)) { - ::get_components_box(self, func) + ::get_components_box(*self, func) } fn get_components_self(self, mut func: impl FnMut(*mut u8)) From 8322460288ae40e5bd96b3775363ba41dce4b5c7 Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Mon, 17 Jan 2022 22:07:37 +0000 Subject: [PATCH 6/6] Address review comments --- crates/bevy_ecs/src/bundle.rs | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index 2189f95e2faeb..69026048edb38 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -22,7 +22,31 @@ use std::{any::TypeId, collections::HashMap}; /// particularly useful for spawning multiple empty entities by using /// [`Commands::spawn_batch`](crate::system::Commands::spawn_batch). /// -/// To use a bundle dynamically, use [`Box`](`ApplicableBundle`) +/// This trait is not object-safe as the components added by a bundle are cached by the type +/// of the [`Bundle`]. To create a version of a bundle which can be used dynamically, use +/// [`Box`](`ApplicableBundle`): +/// +/// ```rust +/// # use bevy_ecs::bundle::{ApplicableBundle, Bundle}; +/// # #[derive(Bundle)] +/// # struct EnemyShip {} +/// # #[derive(Bundle)] +/// # struct TradePost {} +/// /// # use bevy_ecs::bundle::Bundle; +/// # let hostility = 10; +/// let result: Box = if hostility > 5 { +/// Box::new(TradePost { /* ... */}) +/// } else { +/// Box::new(EnemyShip { /* ... */}) +/// }; +/// # if false { +/// # let commands: bevy_ecs::system::Commands = panic!(); // For type checking +/// commands.spawn_bundle(result); +/// # } +/// ``` +/// +/// Note that this dynamic bundle cannot be nested within other bundles, again because of +/// the caching features. /// /// # Examples /// @@ -108,7 +132,8 @@ mod sealed { /// # Safety /// The bundle returned from `init_bundle_info` must be the same as used for `get_component_box`. /// -/// This trait is sealed and cannot be implemented outside of `bevy_ecs` +/// This trait is sealed and cannot be implemented outside of `bevy_ecs`, since it +/// is not useful to implement for custom types. /// /// However, it is implemented for every type which implements [`Bundle`] pub unsafe trait ApplicableBundle: