Skip to main content

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}