dungeon_cell/
lifetime_type_id.rs

1//! [`TypeId`] for types with a lifetime.
2//!
3//! [`TypeId`] only exists for `'static` types. This restricts the types
4//! a [`DungeonCore`][crate::DungeonCore] can hold to '`static` types by default.
5//! However, this module implements a technique inspired by the [`better_any`](https://docs.rs/better_any/latest/better_any/index.html) crate.
6//! With this technique, we can give a `T`, with a single lifetime parameter, a `'static`
7//! marker type and take the [`TypeId`] of that marker. The [`LifetimeTypeId`] type
8//! also wraps around the [`TypeId`] this gives to have the borrow checker verify the
9//! lifetime.
10
11use core::any::TypeId;
12use core::cmp::Ordering;
13use core::marker::PhantomData;
14
15/// Unique identifier for a type with zero or one lifetimes.
16///
17/// If two [`LifetimeTypeId`] are equal with `==` then the type they represent
18/// is identical including the lifetime. This is possible because we use the borrow
19/// checker to prove that `'a` is the same `'a` for both the left and right hand side
20/// of the `==`. If the borrow checker can't prove they are the same then it won't let
21/// the code compile. This is needed because [`TypeId`] can't encode lifetime information
22/// for inspection at runtime.
23///
24/// This type stores the [`TypeId`] for the static form of a type given by [`StaticForm`].
25#[derive(Copy, Clone, Debug, Eq, Hash)]
26pub struct LifetimeTypeId<'a> {
27    /// Type ID of the static form of the type.
28    static_type_id: TypeId,
29
30    /// Marker to hold the lifetime.
31    ///
32    /// This must be invariant over `'a` to make equality correct.
33    ///
34    /// The marker used is taken from:
35    /// <https://doc.rust-lang.org/nomicon/phantom-data.html#table-of-phantomdata-patterns>
36    _marker: PhantomData<core::cell::Cell<&'a ()>>,
37}
38
39impl<'a> LifetimeTypeId<'a> {
40    /// Get the [`LifetimeTypeId`] for a `T`.
41    ///
42    /// The `'a` lifetime will be connected to the single lifetime of `T` if it has one.
43    ///
44    /// Types must enroll into this system by implementing [`StaticForm`].
45    ///
46    /// # Examples
47    ///
48    /// ```
49    /// use dungeon_cell::lifetime_type_id::{LifetimeTypeId};
50    ///
51    /// assert_eq!(LifetimeTypeId::of::<&i32>(), LifetimeTypeId::of::<&i32>());
52    /// assert_ne!(LifetimeTypeId::of::<&i32>(), LifetimeTypeId::of::<i32>());
53    /// ```
54    ///
55    /// ```compile_fail
56    /// use dungeon_cell::lifetime_type_id::{LifetimeTypeId, StaticForm};
57    ///
58    /// let foo = 123i32;
59    /// let mut foo_borrow = &foo;
60    ///
61    /// {
62    ///     let bar = 456i32;
63    ///     let mut bar_borrow = &bar;
64    ///     
65    ///     // forces foo_borrow and bar_borrow to have the same lifetime
66    ///     assert_eq!(of_value(&mut foo_borrow), of_value(&mut bar_borrow));
67    /// }
68    ///
69    /// foo_borrow.set(42);
70    ///
71    /// // helper to get LifetimeTypeId from value
72    /// fn of_value<'a, T: StaticForm<'a>>(_: &'_ mut T) -> LifetimeTypeId<'a> {
73    ///     LifetimeTypeId::of::<T>()
74    /// }
75    /// ```
76    pub fn of<T: StaticForm<'a>>() -> Self {
77        Self {
78            static_type_id: TypeId::of::<T::Static>(),
79            _marker: PhantomData,
80        }
81    }
82}
83
84impl<'a> PartialEq for LifetimeTypeId<'a> {
85    fn eq(&self, other: &Self) -> bool {
86        // Because `LifetimeTypeId` is invariant over `'a` we know that both
87        // `self` and `other` have the same lifetime parameter and we know that
88        // all types that implement `StaticForm<'a>` are unique except for the
89        // lifetime parameter.
90        //
91        // Because of these two things we know the types must be the same including
92        // the lifetime.
93
94        self.static_type_id == other.static_type_id
95    }
96}
97
98impl<'a> PartialOrd for LifetimeTypeId<'a> {
99    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
100        Some(self.cmp(other))
101    }
102}
103
104impl<'a> Ord for LifetimeTypeId<'a> {
105    fn cmp(&self, other: &Self) -> Ordering {
106        // See `PartialEq::eq` for why equality works.
107
108        self.static_type_id.cmp(&other.static_type_id)
109    }
110}
111
112/// Static form of a type with zero or one lifetimes.
113///
114/// The [`Self::Static`] type will be a `'static` marker type unique to [`Self`] minus the
115/// lifetime. [`Self`] must live for the lifetime `'a` for this trait to be implemented
116/// on it. This allows for implementations to tie `'a` to the lifetime parameter of [`Self`] if it
117/// has one.
118///
119/// The [`has_static_form`][crate::has_static_form] macro can implement this trait for most types in a safe way.
120///
121/// # Limitations
122///
123/// [`StaticForm`] cannot be implemented automatically for `T: 'static`. Instead you must
124/// use [`has_static_form`][crate::has_static_form] to implement [`StaticForm`] for the type or a wrapper around the type.
125///
126/// # Safety
127/// [`Self::Static`] must be unique for a given [`Self`] type minus lifetimes.
128pub unsafe trait StaticForm<'a>: 'a {
129    /// Static marker type for [`Self`].
130    type Static: 'static;
131}
132
133/// Implement [`StaticForm`] for type.
134///
135/// This macro uses a subset of the `impl` syntax.
136/// However, you do not need to name the [`StaticForm`] trait in the macro call or provide a body.
137/// Additionally, a `'a` lifetime (it doesn't have to be literally `'a`) is required as the first
138/// generic.
139///
140/// All generics will automatically be bounded by `StaticForm<'a>`. To opt out of this
141/// use `T: 'static` in the `impl` generic definitions. Additionally, extra trait bounds
142/// can be placed in a `where` clause at the end.
143///
144/// See the examples below for the different ways to invoke the macro.
145///
146/// # Limitations
147///
148/// This macro can only implement [`StaticForm`] for nameable types. For opaque types
149/// you must use the [`give_opaque_static_form`][crate::give_opaque_static_form] macro to wrap a specific value.
150///
151/// # Examples
152/// ```
153/// use dungeon_cell::has_static_form;
154/// use dungeon_cell::lifetime_type_id::LifetimeTypeId;
155///
156/// // no lifetimes and no generics
157/// struct BasicType;
158/// has_static_form!(impl<'a> BasicType);
159/// # let _ = LifetimeTypeId::of::<BasicType>();
160///
161/// // one generic
162/// struct OneGeneric<T>(T);
163/// has_static_form!(impl<'a, T> OneGeneric<T>);
164/// # let _ = LifetimeTypeId::of::<OneGeneric<()>>();
165///
166/// // multiple generics
167/// struct MultipleGeneric<A, B, C>(A, B, C);
168/// has_static_form!(impl<'a, A, B, C> MultipleGeneric<A, B, C>);
169/// # let _ = LifetimeTypeId::of::<MultipleGeneric<(), (), ()>>();
170///
171/// // a lifetime
172/// struct Lifetime<'a>(&'a str);
173/// has_static_form!(impl<'a> Lifetime<'a>);
174/// # let _ = LifetimeTypeId::of::<Lifetime<'static>>();
175///
176/// // a lifetime and a generic
177/// struct LifetimeGeneric<'a, T>(&'a T);
178/// has_static_form!(impl<'a, T> LifetimeGeneric<'a, T>);
179/// # let _ = LifetimeTypeId::of::<LifetimeGeneric<'static, ()>>();
180///
181/// // a lifetime and a static generic
182/// struct StaticGeneric<'a, T>(&'a T);
183/// has_static_form!(impl<'a, T: 'static> StaticGeneric<'a, T>);
184///
185/// struct WithoutStaticForm;
186/// let _ = LifetimeTypeId::of::<StaticGeneric<WithoutStaticForm>>();
187///
188/// // extra trait bounds
189/// struct AlwaysCopy<T: Copy>(T);
190/// has_static_form!(impl<'a, T> AlwaysCopy<T> where T: Copy);
191/// # let _ = LifetimeTypeId::of::<AlwaysCopy<i32>>();
192/// ```
193#[macro_export]
194macro_rules! has_static_form {
195    (impl $($tail:tt)+) => {
196        $crate::has_static_form_helper!(
197            @filter_static
198            static
199            impl $($tail)+
200        );
201    };
202}
203
204/// `tt` muncher for the [`has_static_form`] macro.
205///
206/// This helper will filter out `: 'static` generics then find
207/// the `where` clause if it exists.
208#[doc(hidden)]
209#[macro_export]
210macro_rules! has_static_form_helper {
211    (impl $($tail:tt)+) => {
212        $crate::has_static_form_helper!(
213            @filter_static
214            static
215            impl $($tail)+
216        );
217    };
218    (
219        @filter_static
220        $(,$generic:ident)*
221        static $(,$static_generic:ident)*
222        impl<$lt:lifetime, $token:ident : 'static $($tail:tt)+
223    ) => {
224        $crate::has_static_form_helper!(
225            @filter_static
226            $(,$generic)*
227            static $(,$static_generic)* ,$token
228            impl<$lt $($tail)+
229        );
230    };
231    (
232        @filter_static
233        $(,$generic:ident)*
234        static $(,$static_generic:ident)*
235        impl<$lt:lifetime, $token:ident $($tail:tt)+
236    ) => {
237        $crate::has_static_form_helper!(
238            @filter_static
239            $(,$generic)* ,$token
240            static $(,$static_generic)*
241            impl<$lt $($tail)+
242        );
243    };
244    (
245        @filter_static
246        $(,$generic:ident)*
247        static $(,$static_generic:ident)*
248        impl<$lt:lifetime> $($tail:tt)+
249    ) => {
250        $crate::has_static_form_helper!(
251            @filter_where
252            $(,$generic)*
253            static $(,$static_generic)*
254            impl<$lt> {} $($tail)+
255        );
256    };
257    (
258        @filter_where
259        $(,$generic:ident)*
260        static $(,$static_generic:ident)*
261        impl<$lt:lifetime> { $($type:tt)* } where $($tail:tt)*
262    ) => {
263        const _: () = {
264            use ::core::marker::PhantomData;
265
266            pub struct __Marker<$lt $(,$generic: ?Sized)* $(,$static_generic: ?Sized)*>(PhantomData<&$lt()> $(,PhantomData<$generic>)* $(,PhantomData<$static_generic>)*);
267
268            // SAFETY:
269            unsafe impl<
270                $lt
271                $(,$generic: $crate::lifetime_type_id::StaticForm<$lt>)*
272                $(,$static_generic: 'static)*
273            > $crate::lifetime_type_id::StaticForm<$lt> for $($type)*
274            where
275                $($tail)*
276            {
277                type Static = __Marker<'static $(,$generic::Static)* $(,$static_generic)*>;
278            }
279        };
280    };
281    (
282        @filter_where
283        $(,$generic:ident)*
284        static $(,$static_generic:ident)*
285        impl<$lt:lifetime> { $($type:tt)* } $token:tt $($tail:tt)*
286    ) => {
287        $crate::has_static_form_helper!(
288            @filter_where
289            $(,$generic)*
290            static $(,$static_generic)*
291            impl<$lt> { $($type)* $token } $($tail)*
292        );
293    };
294    (
295        @filter_where
296        $(,$generic:ident)*
297        static $(,$static_generic:ident)*
298        impl<$lt:lifetime> { $($type:tt)* }
299    ) => {
300        const _: () = {
301            use ::core::marker::PhantomData;
302
303            pub struct __Marker<$lt $(,$generic: ?Sized)* $(,$static_generic: ?Sized)*>(PhantomData<&$lt()> $(,PhantomData<$generic>)* $(,PhantomData<$static_generic>)*);
304
305            // SAFETY: The static is unique because we used __marker.
306            unsafe impl<
307                $lt
308                $(,$generic: $crate::lifetime_type_id::StaticForm<$lt>)*
309                $(,$static_generic: 'static)*
310            > $crate::lifetime_type_id::StaticForm<$lt> for $($type)* {
311                type Static = __Marker<'static $(,$generic::Static)* $(,$static_generic)*>;
312            }
313        };
314    };
315}
316
317/// Wrap opaque typed value to implement [`StaticForm`].
318///
319/// This macro generates a `#[repr(transparent)]` wrapper struct
320/// that implements [`StaticForm`]. The resulting wrapper is also
321/// an opaque type.
322///
323/// Use the `value.0` accessor to get the original value.
324///
325/// # Examples
326/// ```
327/// use dungeon_cell::give_opaque_static_form;
328/// use dungeon_cell::lifetime_type_id::{LifetimeTypeId, StaticForm};
329///
330/// let mut a = 42;
331/// let b = &mut a;
332///
333/// // closures have an opaque type
334/// let x = || *b += 1;
335///
336/// // wrap the closure so it works with of_value
337/// let mut z = give_opaque_static_form!(x);
338///
339/// // we can still access the closure.
340/// z();
341///
342/// // the type ID of the closure isn't the same as a i32
343/// assert_ne!(of_value(&z), of_value(&0));
344///
345/// // calling the closure changed the variable so it has a lifetime
346/// assert_eq!(a, 43);
347///
348/// fn of_value<'a, T: StaticForm<'a>>(_: &T) -> LifetimeTypeId<'a> {
349///     LifetimeTypeId::of::<T>()
350/// }
351/// ```
352#[macro_export]
353macro_rules! give_opaque_static_form {
354    ($value:expr) => {{
355        // This type will act as a opaque type from the outside of the macro scope.
356        #[repr(transparent)]
357        struct __Wrapper<T>(pub T);
358
359        // SAFETY: The type `Wrapper` cannot be named outside of the macro scope.
360        // Because of this no other uses of __Wrapper can exist.
361        unsafe impl<'a, T: 'a> $crate::lifetime_type_id::StaticForm<'a> for __Wrapper<T> {
362            // This will be unique to the type of `$value`.
363            type Static = __Wrapper<()>;
364        }
365
366        impl<T> ::core::ops::Deref for __Wrapper<T> {
367            type Target = T;
368
369            fn deref(&self) -> &Self::Target {
370                &self.0
371            }
372        }
373
374        impl<T> ::core::ops::DerefMut for __Wrapper<T> {
375            fn deref_mut(&mut self) -> &mut Self::Target {
376                &mut self.0
377            }
378        }
379
380        // Have type inference construct a new opaque type for us.
381        __Wrapper($value)
382    }}
383}
384
385// SAFETY: The static is unique minus the lifetime.
386unsafe impl<'a, T: StaticForm<'a>, const N: usize> StaticForm<'a> for [T; N] {
387    type Static = [T::Static; N];
388}
389
390has_static_form!(impl<'a> bool);
391has_static_form!(impl<'a> char);
392has_static_form!(impl<'a> f32);
393has_static_form!(impl<'a> f64);
394
395has_static_form!(impl<'a, R> fn() -> R);
396has_static_form!(impl<'a, A1, R> fn(A1) -> R);
397has_static_form!(impl<'a, A1, A2, R> fn(A1, A2) -> R);
398
399has_static_form!(impl<'a> i8);
400has_static_form!(impl<'a> i16);
401has_static_form!(impl<'a> i32);
402has_static_form!(impl<'a> i64);
403has_static_form!(impl<'a> i128);
404has_static_form!(impl<'a> isize);
405
406has_static_form!(impl<'a, T> *const T where T: ?Sized);
407has_static_form!(impl<'a, T> *mut T where T: ?Sized);
408
409has_static_form!(impl<'a, T> &'a T where T: ?Sized);
410has_static_form!(impl<'a, T> &'a mut T where T: ?Sized);
411
412has_static_form!(impl<'a, T> [T]);
413has_static_form!(impl<'a> str);
414
415has_static_form!(impl<'a> ());
416has_static_form!(impl<'a, A> (A,));
417has_static_form!(impl<'a, A, B> (A, B));
418
419has_static_form!(impl<'a> u8);
420has_static_form!(impl<'a> u16);
421has_static_form!(impl<'a> u32);
422has_static_form!(impl<'a> u64);
423has_static_form!(impl<'a> u128);
424has_static_form!(impl<'a> usize);