bevy_ecs/label.rs
1//! Traits used by label implementations
2
3use core::{
4 any::Any,
5 hash::{Hash, Hasher},
6};
7
8// Re-exported for use within `define_label!`
9#[doc(hidden)]
10pub use alloc::boxed::Box;
11
12/// An object safe version of [`Eq`]. This trait is automatically implemented
13/// for any `'static` type that implements `Eq`.
14pub trait DynEq: Any {
15 /// This method tests for `self` and `other` values to be equal.
16 ///
17 /// Implementers should avoid returning `true` when the underlying types are
18 /// not the same.
19 fn dyn_eq(&self, other: &dyn DynEq) -> bool;
20}
21
22// Tests that this trait is dyn-compatible
23const _: Option<Box<dyn DynEq>> = None;
24
25impl<T> DynEq for T
26where
27 T: Any + Eq,
28{
29 fn dyn_eq(&self, other: &dyn DynEq) -> bool {
30 if let Some(other) = (other as &dyn Any).downcast_ref::<T>() {
31 return self == other;
32 }
33 false
34 }
35}
36
37/// An object safe version of [`Hash`]. This trait is automatically implemented
38/// for any `'static` type that implements `Hash`.
39pub trait DynHash: DynEq {
40 /// Feeds this value into the given [`Hasher`].
41 fn dyn_hash(&self, state: &mut dyn Hasher);
42}
43
44// Tests that this trait is dyn-compatible
45const _: Option<Box<dyn DynHash>> = None;
46
47impl<T> DynHash for T
48where
49 T: DynEq + Hash,
50{
51 fn dyn_hash(&self, mut state: &mut dyn Hasher) {
52 T::hash(self, &mut state);
53 self.type_id().hash(&mut state);
54 }
55}
56
57/// Macro to define a new label trait.
58///
59/// Each label trait has an associated [`Interner<dyn YourLabelTraitHere>`][crate::intern::Interner]
60/// The trait has an `intern(&self)` method which uses that interner to
61/// produce [`Interned<dyn YourLabelTraitHere>`][crate::intern::Interned] values,
62/// and a `dyn_clone(&self)` method which must be implemented for the system to work.
63///
64/// # Examples
65///
66/// Minimal working example:
67///
68/// ```
69/// # use bevy_ecs::define_label;
70/// // Defines `trait MyNewLabelTrait` and `static MY_NEW_LABEL_TRAIT_INTERNER`.
71/// // You don’t need to use the interner for anything; just give it a unique name.
72/// define_label!(
73/// /// Documentation of label trait
74/// MyNewLabelTrait,
75/// );
76///
77/// /// A new label type implementing the new label trait.
78/// #[derive(Clone, Debug, Eq, Hash, PartialEq)]
79/// pub struct MyLabel;
80///
81/// impl MyNewLabelTrait for MyLabel {
82/// // Implementations of the trait must implement the `dyn_clone()` method in this way
83/// // to enable cloning the trait object because `Clone` is not `dyn` compatible.
84/// fn dyn_clone(&self) -> Box<dyn MyNewLabelTrait> {
85/// Box::new(self.clone())
86/// }
87/// }
88///
89/// assert_eq!(MyLabel.intern(), MyLabel.intern());
90/// ```
91///
92/// A label trait defined by this macro can also be given additional methods:
93///
94/// ```
95/// # use bevy_ecs::define_label;
96/// define_label!(
97/// /// Documentation of another label trait
98/// MyNewExtendedLabelTrait,
99/// extra_methods: {
100/// // Extra methods for the trait can be defined here
101/// fn additional_method(&self) -> i32;
102/// },
103/// extra_methods_impl: {
104/// // Implementation of the extra methods for Interned<dyn MyNewExtendedLabelTrait>,
105/// // which should usually forward to the contained value.
106/// fn additional_method(&self) -> i32 {
107/// (**self).additional_method()
108/// }
109/// }
110/// );
111///
112/// #[derive(Clone, Debug, Eq, Hash, PartialEq)]
113/// pub struct MyLabel;
114///
115/// impl MyNewExtendedLabelTrait for MyLabel {
116/// fn dyn_clone(&self) -> Box<dyn MyNewExtendedLabelTrait> {
117/// Box::new(self.clone())
118/// }
119///
120/// fn additional_method(&self) -> i32 {
121/// 42
122/// }
123/// }
124///
125/// let interned_label = MyLabel.intern();
126/// assert_eq!(interned_label.additional_method(), 42);
127/// ```
128///
129/// In order to minimize boilerplate for each new label type, you may wish to define a macro to
130/// generate labels. In Bevy’s own traits, this is done by derive macros (e.g.
131/// `derive(ScheduleLabel)`), but it is often sufficient to write a simple, less general
132/// `macro_rules!` macro:
133///
134/// ```
135/// # use bevy_ecs::define_label;
136/// define_label!(Team);
137///
138/// macro_rules! define_team {
139/// ($name:ident) => {
140/// #[derive(Clone, Debug, Eq, Hash, PartialEq)]
141/// pub struct $name;
142///
143/// impl Team for $name {
144/// fn dyn_clone(&self) -> Box<dyn Team> {
145/// Box::new(self.clone())
146/// }
147/// }
148/// }
149/// }
150///
151/// define_team!(Home);
152/// define_team!(Away);
153///
154/// assert_eq!(Home.intern(), Home.intern());
155/// assert_ne!(Home.intern(), Away.intern());
156/// ```
157///
158#[macro_export]
159macro_rules! define_label {
160 (
161 $(#[$label_attr:meta])*
162 $label_trait_name:ident $(,)?
163 ) => {
164 $crate::define_label!(
165 $(#[$label_attr])*
166 $label_trait_name,
167 extra_methods: {},
168 extra_methods_impl: {}
169 );
170 };
171 (
172 $(#[$label_attr:meta])*
173 $label_trait_name:ident,
174 extra_methods: { $($trait_extra_methods:tt)* },
175 extra_methods_impl: { $($interned_extra_methods_impl:tt)* } $(,)?
176 ) => {
177
178 $(#[$label_attr])*
179 pub trait $label_trait_name: ::core::marker::Send + ::core::marker::Sync + ::core::fmt::Debug + $crate::label::DynEq + $crate::label::DynHash {
180
181 $($trait_extra_methods)*
182
183 /// Clones this `
184 #[doc = ::core::stringify!($label_trait_name)]
185 ///`.
186 fn dyn_clone(&self) -> $crate::label::Box<dyn $label_trait_name>;
187
188 /// Returns an [`Interned`] value corresponding to `self`.
189 fn intern(&self) -> $crate::intern::Interned<dyn $label_trait_name>
190 where Self: ::core::marker::Sized {
191 static INTERNER: $crate::intern::Interner<dyn $label_trait_name> =
192 $crate::intern::Interner::new();
193
194 INTERNER.intern(self)
195 }
196 }
197
198 #[diagnostic::do_not_recommend]
199 impl $label_trait_name for $crate::intern::Interned<dyn $label_trait_name> {
200
201 $($interned_extra_methods_impl)*
202
203 fn dyn_clone(&self) -> $crate::label::Box<dyn $label_trait_name> {
204 (**self).dyn_clone()
205 }
206
207 fn intern(&self) -> Self {
208 *self
209 }
210 }
211
212 impl ::core::cmp::PartialEq for dyn $label_trait_name {
213 fn eq(&self, other: &Self) -> bool {
214 self.dyn_eq(other)
215 }
216 }
217
218 impl ::core::cmp::Eq for dyn $label_trait_name {}
219
220 impl ::core::hash::Hash for dyn $label_trait_name {
221 fn hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
222 self.dyn_hash(state);
223 }
224 }
225
226 impl $crate::intern::Internable for dyn $label_trait_name {
227 fn leak(&self) -> &'static Self {
228 $crate::label::Box::leak(self.dyn_clone())
229 }
230
231 fn ref_eq(&self, other: &Self) -> bool {
232 use ::core::ptr;
233
234 // Test that both the type id and pointer address are equivalent.
235 self.type_id() == other.type_id()
236 && ptr::addr_eq(ptr::from_ref::<Self>(self), ptr::from_ref::<Self>(other))
237 }
238
239 fn ref_hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
240 use ::core::{hash::Hash, ptr};
241
242 // Hash the type id...
243 self.type_id().hash(state);
244
245 // ...and the pointer address.
246 // Cast to a unit `()` first to discard any pointer metadata.
247 ptr::from_ref::<Self>(self).cast::<()>().hash(state);
248 }
249 }
250 };
251}