Skip to content

Commit dc40cd1

Browse files
Remove ComponentStorage and associated types (#12311)
# Objective When doing a final pass for #3362, it appeared that `ComponentStorage` as a trait, the two types implementing it, and the associated type on `Component` aren't really necessary anymore. This likely was due to an earlier constraint on the use of consts in traits, but that definitely doesn't seem to be a problem in Rust 1.76. ## Solution Remove them. --- ## Changelog Changed: `Component::Storage` has been replaced with `Component::STORAGE_TYPE` as a const. Removed: `bevy::ecs::component::ComponentStorage` trait Removed: `bevy::ecs::component::TableStorage` struct Removed: `bevy::ecs::component::SparseSetStorage` struct ## Migration Guide If you were manually implementing `Component` instead of using the derive macro, replace the associated `Storage` associated type with the `STORAGE_TYPE` const: ```rust // in Bevy 0.13 impl Component for MyComponent { type Storage = TableStorage; } // in Bevy 0.14 impl Component for MyComponent { const STORAGE_TYPE: StorageType = StorageType::Table; } ``` Component is no longer object safe. If you were relying on `&dyn Component`, `Box<dyn Component>`, etc. please [file an issue ](https://github.com/bevyengine/bevy/issues) to get [this change](#12311) reverted. --------- Co-authored-by: Alice Cecile <[email protected]>
1 parent 70b70cd commit dc40cd1

File tree

7 files changed

+44
-75
lines changed

7 files changed

+44
-75
lines changed

crates/bevy_ecs/macros/src/component.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
6060

6161
TokenStream::from(quote! {
6262
impl #impl_generics #bevy_ecs_path::component::Component for #struct_name #type_generics #where_clause {
63-
type Storage = #storage;
63+
const STORAGE_TYPE: #bevy_ecs_path::component::StorageType = #storage;
6464
}
6565
})
6666
}
@@ -110,10 +110,10 @@ fn parse_component_attr(ast: &DeriveInput) -> Result<Attrs> {
110110
}
111111

112112
fn storage_path(bevy_ecs_path: &Path, ty: StorageTy) -> TokenStream2 {
113-
let typename = match ty {
114-
StorageTy::Table => Ident::new("TableStorage", Span::call_site()),
115-
StorageTy::SparseSet => Ident::new("SparseStorage", Span::call_site()),
113+
let storage_type = match ty {
114+
StorageTy::Table => Ident::new("Table", Span::call_site()),
115+
StorageTy::SparseSet => Ident::new("SparseSet", Span::call_site()),
116116
};
117117

118-
quote! { #bevy_ecs_path::component::#typename }
118+
quote! { #bevy_ecs_path::component::StorageType::#storage_type }
119119
}

crates/bevy_ecs/src/bundle.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::{
1010
AddBundle, Archetype, ArchetypeId, Archetypes, BundleComponentStatus, ComponentStatus,
1111
SpawnBundleStatus,
1212
},
13-
component::{Component, ComponentId, ComponentStorage, Components, StorageType, Tick},
13+
component::{Component, ComponentId, Components, StorageType, Tick},
1414
entity::{Entities, Entity, EntityLocation},
1515
prelude::World,
1616
query::DebugCheckedUnwrap,
@@ -203,7 +203,7 @@ unsafe impl<C: Component> Bundle for C {
203203
impl<C: Component> DynamicBundle for C {
204204
#[inline]
205205
fn get_components(self, func: &mut impl FnMut(StorageType, OwningPtr<'_>)) {
206-
OwningPtr::make(self, |ptr| func(C::Storage::STORAGE_TYPE, ptr));
206+
OwningPtr::make(self, |ptr| func(C::STORAGE_TYPE, ptr));
207207
}
208208
}
209209

crates/bevy_ecs/src/component.rs

Lines changed: 4 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -151,42 +151,13 @@ use std::{
151151
/// [`SyncCell`]: bevy_utils::synccell::SyncCell
152152
/// [`Exclusive`]: https://doc.rust-lang.org/nightly/std/sync/struct.Exclusive.html
153153
pub trait Component: Send + Sync + 'static {
154-
/// A marker type indicating the storage type used for this component.
155-
/// This must be either [`TableStorage`] or [`SparseStorage`].
156-
type Storage: ComponentStorage;
154+
/// A constant indicating the storage type used for this component.
155+
const STORAGE_TYPE: StorageType;
157156

158157
/// Called when registering this component, allowing mutable access to it's [`ComponentHooks`].
159158
fn register_component_hooks(_hooks: &mut ComponentHooks) {}
160159
}
161160

162-
/// Marker type for components stored in a [`Table`](crate::storage::Table).
163-
pub struct TableStorage;
164-
165-
/// Marker type for components stored in a [`ComponentSparseSet`](crate::storage::ComponentSparseSet).
166-
pub struct SparseStorage;
167-
168-
/// Types used to specify the storage strategy for a component.
169-
///
170-
/// This trait is implemented for [`TableStorage`] and [`SparseStorage`].
171-
/// Custom implementations are forbidden.
172-
pub trait ComponentStorage: sealed::Sealed {
173-
/// A value indicating the storage strategy specified by this type.
174-
const STORAGE_TYPE: StorageType;
175-
}
176-
177-
impl ComponentStorage for TableStorage {
178-
const STORAGE_TYPE: StorageType = StorageType::Table;
179-
}
180-
impl ComponentStorage for SparseStorage {
181-
const STORAGE_TYPE: StorageType = StorageType::SparseSet;
182-
}
183-
184-
mod sealed {
185-
pub trait Sealed {}
186-
impl Sealed for super::TableStorage {}
187-
impl Sealed for super::SparseStorage {}
188-
}
189-
190161
/// The storage used for a specific component type.
191162
///
192163
/// # Examples
@@ -472,7 +443,7 @@ impl ComponentDescriptor {
472443
pub fn new<T: Component>() -> Self {
473444
Self {
474445
name: Cow::Borrowed(std::any::type_name::<T>()),
475-
storage_type: T::Storage::STORAGE_TYPE,
446+
storage_type: T::STORAGE_TYPE,
476447
is_send_and_sync: true,
477448
type_id: Some(TypeId::of::<T>()),
478449
layout: Layout::new::<T>(),
@@ -503,7 +474,7 @@ impl ComponentDescriptor {
503474

504475
/// Create a new `ComponentDescriptor` for a resource.
505476
///
506-
/// The [`StorageType`] for resources is always [`TableStorage`].
477+
/// The [`StorageType`] for resources is always [`StorageType::Table`].
507478
pub fn new_resource<T: Resource>() -> Self {
508479
Self {
509480
name: Cow::Borrowed(std::any::type_name::<T>()),

crates/bevy_ecs/src/query/fetch.rs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::{
22
archetype::Archetype,
33
change_detection::{Ticks, TicksMut},
4-
component::{Component, ComponentId, ComponentStorage, StorageType, Tick},
4+
component::{Component, ComponentId, StorageType, Tick},
55
entity::Entity,
66
query::{Access, DebugCheckedUnwrap, FilteredAccess, WorldQuery},
77
storage::{ComponentSparseSet, Table, TableRow},
@@ -711,9 +711,9 @@ unsafe impl<'a> QueryData for FilteredEntityMut<'a> {
711711

712712
#[doc(hidden)]
713713
pub struct ReadFetch<'w, T> {
714-
// T::Storage = TableStorage
714+
// T::STORAGE_TYPE = StorageType::Table
715715
table_components: Option<ThinSlicePtr<'w, UnsafeCell<T>>>,
716-
// T::Storage = SparseStorage
716+
// T::STORAGE_TYPE = StorageType::SparseSet
717717
sparse_set: Option<&'w ComponentSparseSet>,
718718
}
719719

@@ -747,7 +747,7 @@ unsafe impl<T: Component> WorldQuery for &T {
747747
) -> ReadFetch<'w, T> {
748748
ReadFetch {
749749
table_components: None,
750-
sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet).then(|| {
750+
sparse_set: (T::STORAGE_TYPE == StorageType::SparseSet).then(|| {
751751
// SAFETY: The underlying type associated with `component_id` is `T`,
752752
// which we are allowed to access since we registered it in `update_archetype_component_access`.
753753
// Note that we do not actually access any components in this function, we just get a shared
@@ -764,7 +764,7 @@ unsafe impl<T: Component> WorldQuery for &T {
764764
}
765765

766766
const IS_DENSE: bool = {
767-
match T::Storage::STORAGE_TYPE {
767+
match T::STORAGE_TYPE {
768768
StorageType::Table => true,
769769
StorageType::SparseSet => false,
770770
}
@@ -806,7 +806,7 @@ unsafe impl<T: Component> WorldQuery for &T {
806806
entity: Entity,
807807
table_row: TableRow,
808808
) -> Self::Item<'w> {
809-
match T::Storage::STORAGE_TYPE {
809+
match T::STORAGE_TYPE {
810810
StorageType::Table => {
811811
// SAFETY: STORAGE_TYPE = Table
812812
let table = unsafe { fetch.table_components.debug_checked_unwrap() };
@@ -862,13 +862,13 @@ unsafe impl<T: Component> ReadOnlyQueryData for &T {}
862862

863863
#[doc(hidden)]
864864
pub struct RefFetch<'w, T> {
865-
// T::Storage = TableStorage
865+
// T::STORAGE_TYPE = StorageType::Table
866866
table_data: Option<(
867867
ThinSlicePtr<'w, UnsafeCell<T>>,
868868
ThinSlicePtr<'w, UnsafeCell<Tick>>,
869869
ThinSlicePtr<'w, UnsafeCell<Tick>>,
870870
)>,
871-
// T::Storage = SparseStorage
871+
// T::STORAGE_TYPE = StorageType::SparseSet
872872
sparse_set: Option<&'w ComponentSparseSet>,
873873

874874
last_run: Tick,
@@ -905,7 +905,7 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> {
905905
) -> RefFetch<'w, T> {
906906
RefFetch {
907907
table_data: None,
908-
sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet).then(|| {
908+
sparse_set: (T::STORAGE_TYPE == StorageType::SparseSet).then(|| {
909909
// SAFETY: The underlying type associated with `component_id` is `T`,
910910
// which we are allowed to access since we registered it in `update_archetype_component_access`.
911911
// Note that we do not actually access any components in this function, we just get a shared
@@ -924,7 +924,7 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> {
924924
}
925925

926926
const IS_DENSE: bool = {
927-
match T::Storage::STORAGE_TYPE {
927+
match T::STORAGE_TYPE {
928928
StorageType::Table => true,
929929
StorageType::SparseSet => false,
930930
}
@@ -965,7 +965,7 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> {
965965
entity: Entity,
966966
table_row: TableRow,
967967
) -> Self::Item<'w> {
968-
match T::Storage::STORAGE_TYPE {
968+
match T::STORAGE_TYPE {
969969
StorageType::Table => {
970970
// SAFETY: STORAGE_TYPE = Table
971971
let (table_components, added_ticks, changed_ticks) =
@@ -1045,13 +1045,13 @@ unsafe impl<'__w, T: Component> ReadOnlyQueryData for Ref<'__w, T> {}
10451045

10461046
#[doc(hidden)]
10471047
pub struct WriteFetch<'w, T> {
1048-
// T::Storage = TableStorage
1048+
// T::STORAGE_TYPE = StorageType::Table
10491049
table_data: Option<(
10501050
ThinSlicePtr<'w, UnsafeCell<T>>,
10511051
ThinSlicePtr<'w, UnsafeCell<Tick>>,
10521052
ThinSlicePtr<'w, UnsafeCell<Tick>>,
10531053
)>,
1054-
// T::Storage = SparseStorage
1054+
// T::STORAGE_TYPE = StorageType::SparseSet
10551055
sparse_set: Option<&'w ComponentSparseSet>,
10561056

10571057
last_run: Tick,
@@ -1088,7 +1088,7 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T {
10881088
) -> WriteFetch<'w, T> {
10891089
WriteFetch {
10901090
table_data: None,
1091-
sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet).then(|| {
1091+
sparse_set: (T::STORAGE_TYPE == StorageType::SparseSet).then(|| {
10921092
// SAFETY: The underlying type associated with `component_id` is `T`,
10931093
// which we are allowed to access since we registered it in `update_archetype_component_access`.
10941094
// Note that we do not actually access any components in this function, we just get a shared
@@ -1107,7 +1107,7 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T {
11071107
}
11081108

11091109
const IS_DENSE: bool = {
1110-
match T::Storage::STORAGE_TYPE {
1110+
match T::STORAGE_TYPE {
11111111
StorageType::Table => true,
11121112
StorageType::SparseSet => false,
11131113
}
@@ -1148,7 +1148,7 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T {
11481148
entity: Entity,
11491149
table_row: TableRow,
11501150
) -> Self::Item<'w> {
1151-
match T::Storage::STORAGE_TYPE {
1151+
match T::STORAGE_TYPE {
11521152
StorageType::Table => {
11531153
// SAFETY: STORAGE_TYPE = Table
11541154
let (table_components, added_ticks, changed_ticks) =
@@ -1433,7 +1433,7 @@ unsafe impl<T: Component> WorldQuery for Has<T> {
14331433
}
14341434

14351435
const IS_DENSE: bool = {
1436-
match T::Storage::STORAGE_TYPE {
1436+
match T::STORAGE_TYPE {
14371437
StorageType::Table => true,
14381438
StorageType::SparseSet => false,
14391439
}

crates/bevy_ecs/src/query/filter.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{
22
archetype::Archetype,
3-
component::{Component, ComponentId, ComponentStorage, StorageType, Tick},
3+
component::{Component, ComponentId, StorageType, Tick},
44
entity::Entity,
55
query::{DebugCheckedUnwrap, FilteredAccess, WorldQuery},
66
storage::{Column, ComponentSparseSet, Table, TableRow},
@@ -142,7 +142,7 @@ unsafe impl<T: Component> WorldQuery for With<T> {
142142
}
143143

144144
const IS_DENSE: bool = {
145-
match T::Storage::STORAGE_TYPE {
145+
match T::STORAGE_TYPE {
146146
StorageType::Table => true,
147147
StorageType::SparseSet => false,
148148
}
@@ -250,7 +250,7 @@ unsafe impl<T: Component> WorldQuery for Without<T> {
250250
}
251251

252252
const IS_DENSE: bool = {
253-
match T::Storage::STORAGE_TYPE {
253+
match T::STORAGE_TYPE {
254254
StorageType::Table => true,
255255
StorageType::SparseSet => false,
256256
}
@@ -604,15 +604,15 @@ unsafe impl<T: Component> WorldQuery for Added<T> {
604604
) -> Self::Fetch<'w> {
605605
Self::Fetch::<'w> {
606606
table_ticks: None,
607-
sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet)
607+
sparse_set: (T::STORAGE_TYPE == StorageType::SparseSet)
608608
.then(|| world.storages().sparse_sets.get(id).debug_checked_unwrap()),
609609
last_run,
610610
this_run,
611611
}
612612
}
613613

614614
const IS_DENSE: bool = {
615-
match T::Storage::STORAGE_TYPE {
615+
match T::STORAGE_TYPE {
616616
StorageType::Table => true,
617617
StorageType::SparseSet => false,
618618
}
@@ -651,7 +651,7 @@ unsafe impl<T: Component> WorldQuery for Added<T> {
651651
entity: Entity,
652652
table_row: TableRow,
653653
) -> Self::Item<'w> {
654-
match T::Storage::STORAGE_TYPE {
654+
match T::STORAGE_TYPE {
655655
StorageType::Table => {
656656
// SAFETY: STORAGE_TYPE = Table
657657
let table = unsafe { fetch.table_ticks.debug_checked_unwrap() };
@@ -813,15 +813,15 @@ unsafe impl<T: Component> WorldQuery for Changed<T> {
813813
) -> Self::Fetch<'w> {
814814
Self::Fetch::<'w> {
815815
table_ticks: None,
816-
sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet)
816+
sparse_set: (T::STORAGE_TYPE == StorageType::SparseSet)
817817
.then(|| world.storages().sparse_sets.get(id).debug_checked_unwrap()),
818818
last_run,
819819
this_run,
820820
}
821821
}
822822

823823
const IS_DENSE: bool = {
824-
match T::Storage::STORAGE_TYPE {
824+
match T::STORAGE_TYPE {
825825
StorageType::Table => true,
826826
StorageType::SparseSet => false,
827827
}
@@ -860,7 +860,7 @@ unsafe impl<T: Component> WorldQuery for Changed<T> {
860860
entity: Entity,
861861
table_row: TableRow,
862862
) -> Self::Item<'w> {
863-
match T::Storage::STORAGE_TYPE {
863+
match T::STORAGE_TYPE {
864864
StorageType::Table => {
865865
// SAFETY: STORAGE_TYPE = Table
866866
let table = unsafe { fetch.table_ticks.debug_checked_unwrap() };

crates/bevy_ecs/src/world/unsafe_world_cell.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ use crate::{
77
archetype::{Archetype, ArchetypeComponentId, Archetypes},
88
bundle::Bundles,
99
change_detection::{MutUntyped, Ticks, TicksMut},
10-
component::{
11-
ComponentId, ComponentStorage, ComponentTicks, Components, StorageType, Tick, TickCells,
12-
},
10+
component::{ComponentId, ComponentTicks, Components, StorageType, Tick, TickCells},
1311
entity::{Entities, Entity, EntityLocation},
1412
prelude::Component,
1513
removal_detection::RemovedComponentEvents,
@@ -713,7 +711,7 @@ impl<'w> UnsafeEntityCell<'w> {
713711
get_component(
714712
self.world,
715713
component_id,
716-
T::Storage::STORAGE_TYPE,
714+
T::STORAGE_TYPE,
717715
self.entity,
718716
self.location,
719717
)
@@ -740,7 +738,7 @@ impl<'w> UnsafeEntityCell<'w> {
740738
get_component_and_ticks(
741739
self.world,
742740
component_id,
743-
T::Storage::STORAGE_TYPE,
741+
T::STORAGE_TYPE,
744742
self.entity,
745743
self.location,
746744
)
@@ -770,7 +768,7 @@ impl<'w> UnsafeEntityCell<'w> {
770768
get_ticks(
771769
self.world,
772770
component_id,
773-
T::Storage::STORAGE_TYPE,
771+
T::STORAGE_TYPE,
774772
self.entity,
775773
self.location,
776774
)
@@ -839,7 +837,7 @@ impl<'w> UnsafeEntityCell<'w> {
839837
get_component_and_ticks(
840838
self.world,
841839
component_id,
842-
T::Storage::STORAGE_TYPE,
840+
T::STORAGE_TYPE,
843841
self.entity,
844842
self.location,
845843
)

examples/ecs/component_hooks.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@
1313
//! - Enforcing structural rules: When you have systems that depend on specific relationships
1414
//! between components (like hierarchies or parent-child links) and need to maintain correctness.
1515
16-
use bevy::ecs::component::{ComponentHooks, TableStorage};
16+
use bevy::ecs::component::{ComponentHooks, StorageType};
1717
use bevy::prelude::*;
1818
use std::collections::HashMap;
1919

2020
#[derive(Debug)]
2121
struct MyComponent(KeyCode);
2222

2323
impl Component for MyComponent {
24-
type Storage = TableStorage;
24+
const STORAGE_TYPE: StorageType = StorageType::Table;
2525

2626
/// Hooks can also be registered during component initialisation by
2727
/// implementing `register_component_hooks`

0 commit comments

Comments
 (0)