Skip to main content

typewit/
const_marker.rs

1//! Marker types for passing constants as type arguments.
2//! 
3//! # Example
4//! 
5//! This example emulates specialization,
6//! eliding a `.clone()` call when the created array is only one element long.
7//! 
8//! ```rust
9//! use typewit::{const_marker::Usize, TypeCmp, TypeEq};
10//! 
11//! let arr = [3u8, 5, 8];
12//! 
13//! assert_eq!(repeat(3), []);
14//! assert_eq!(repeat(3), [3]);
15//! assert_eq!(repeat(3), [3, 3]);
16//! assert_eq!(repeat(3), [3, 3, 3]);
17//! assert_eq!(repeat(3), [3, 3, 3, 3]);
18//! 
19//! 
20//! fn repeat<T: Clone, const OUT: usize>(val: T) -> [T; OUT] {
21//!     // `te_len` ìs a `TypeEq<Usize<OUT>, Usize<1>>`
22//!     if let TypeCmp::Eq(te_len) = Usize::<OUT>.equals(Usize::<1>) {
23//!         // This branch is ran when `OUT == 1`
24//!         TypeEq::new::<T>()    // returns `TypeEq<T, T>`
25//!             .in_array(te_len) // returns `TypeEq<[T; OUT], [T; 1]>`
26//!             .to_left([val])   // goes from `[T; 1]` to `[T; OUT]`
27//!     } else {
28//!         // This branch is ran when `OUT != 1`
29//!         [(); OUT].map(|_| val.clone())
30//!     }
31//! }
32//! ```
33//! 
34//! 
35
36use crate::{
37    TypeEq,
38    TypeNe,
39};
40
41mod const_marker_trait;
42
43pub use const_marker_trait::*;
44
45
46#[cfg(feature = "rust_1_83")]
47mod const_marker_eq_traits;
48
49#[cfg(feature = "rust_1_83")]
50pub use const_marker_eq_traits::*;
51
52
53#[cfg(feature = "serde")]
54mod const_marker_serde_impls;
55
56#[cfg(feature = "serde")]
57pub(crate) use const_marker_serde_impls::*;
58
59
60mod boolwit;
61
62pub use boolwit::*;
63
64
65#[cfg(feature = "adt_const_marker")]
66mod slice_const_markers;
67
68#[cfg(feature = "adt_const_marker")]
69#[cfg_attr(feature = "docsrs", doc(cfg(feature = "adt_const_marker")))]
70pub use slice_const_markers::Str;
71
72/// Marker types for `const FOO: &'static [T]` parameters.
73#[cfg(feature = "adt_const_marker")]
74#[cfg_attr(feature = "docsrs", doc(cfg(feature = "adt_const_marker")))]
75pub mod slice {
76    pub use super::slice_const_markers::{
77        BoolSlice,
78        CharSlice,
79        U8Slice,
80        U16Slice,
81        U32Slice,
82        U64Slice,
83        U128Slice,
84        UsizeSlice,
85        I8Slice,
86        I16Slice,
87        I32Slice,
88        I64Slice,
89        I128Slice,
90        IsizeSlice,
91        StrSlice,
92    };
93}
94
95struct Helper<L, R>(L, R);
96
97
98macro_rules! __const_eq_with {
99    ($L:expr, $R:expr) => {
100        $L == $R
101    };
102    ($L:expr, $R:expr, ($L2:ident, $R2:ident) $cmp:expr) => ({
103        let $L2 = $L;
104        let $R2 = $R;
105        $cmp
106    });
107} pub(crate) use __const_eq_with;
108
109
110macro_rules! declare_const_param_type {
111    ($($params:tt)*) => {
112        $crate::const_marker::__declare_const_param_type!{ $($params)* }
113
114        #[cfg(feature = "rust_1_83")]
115        $crate::const_marker::__const_marker_impls!{ $($params)* }
116    }
117} pub(crate) use declare_const_param_type;
118
119
120macro_rules! __declare_const_param_type {
121    (
122        $(#[$struct_docs:meta])*
123        $struct:ident($prim:ty) 
124        $deser_type:ident,
125
126        $(
127            $(#[$eq_docs:meta])*
128            fn equals $(($L:ident, $R:ident) $comparator:block)?;
129        )?
130    ) => {
131        #[doc = concat!(
132            "Marker type for passing `const VAL: ", stringify!($prim),
133            "` as a type parameter."
134        )]
135        /// # Serde compatibility
136        /// 
137        /// When the `"serde"` feature is enabled, 
138        /// this type is serialized/deserialized as the `VAL` const parameter.
139        /// 
140        #[cfg_attr(
141            all(feature = "serde", feature = "serde_json"),
142            doc = $crate::const_marker::__const_marker_deserialize_doc_example!(
143                $deser_type
144                $struct
145                "rust"
146            )
147        )]
148        #[cfg_attr(
149            not(all(feature = "serde", feature = "serde_json")),
150            doc = $crate::const_marker::__const_marker_deserialize_doc_example!(
151                $deser_type
152                $struct
153                "ignore"
154            )
155        )]
156        /// 
157        $(#[$struct_docs])*
158        #[derive(Copy, Clone)]
159        pub struct $struct<const VAL: $prim>;
160
161        impl<const VAL: $prim> crate::const_marker::ConstMarker for $struct<VAL> {
162            const VAL: Self::Of = VAL;
163            type Of = $prim;
164        }
165
166        impl<const L: $prim, const R: $prim> $crate::const_marker::Helper<$struct<L>, $struct<R>> {
167            const EQ: Result<
168                TypeEq<$struct<L>, $struct<R>>,
169                TypeNe<$struct<L>, $struct<R>>,
170            > = if crate::const_marker::__const_eq_with!(
171                L,
172                R
173                $($(, ($L, $R) $comparator)?)?
174            ) {
175                // SAFETY: `L == R` (both are std types with sensible Eq impls)
176                // therefore `$struct<L> == $struct<R>`
177                unsafe {
178                    Ok(TypeEq::<$struct<L>, $struct<R>>::new_unchecked())
179                }
180            } else {
181                // SAFETY: `L != R` (both are std types with sensible Eq impls)
182                // therefore `$struct<L> != $struct<R>`
183                unsafe {
184                    Err(TypeNe::<$struct<L>, $struct<R>>::new_unchecked())
185                }
186            };
187
188            const EQUALS: crate::TypeCmp<$struct<L>, $struct<R>> = match Self::EQ {
189                Ok(x) => crate::TypeCmp::Eq(x),
190                Err(x) => crate::TypeCmp::Ne(x),
191            };
192        }
193
194        impl<const VAL: $prim> $struct<VAL> {
195            /// Compares `self` and `other` for equality.
196            ///
197            /// Returns:
198            /// - `Ok(TypeEq)`: if `VAL == OTHER`
199            /// - `Err(TypeNe)`: if `VAL != OTHER`
200            ///
201            #[inline(always)]
202            #[deprecated(note = "superceeded by `equals` method", since = "1.8.0")]
203            pub const fn eq<const OTHER: $prim>(
204                self, 
205                _other: $struct<OTHER>,
206            ) -> Result<
207                TypeEq<$struct<VAL>, $struct<OTHER>>,
208                TypeNe<$struct<VAL>, $struct<OTHER>>,
209            > {
210                $crate::const_marker::Helper::<$struct<VAL>, $struct<OTHER>>::EQ
211            }
212
213            /// Compares `self` and `other` for equality.
214            ///
215            /// Returns:
216            /// - `TypeCmp::Eq(TypeEq)`: if `VAL == OTHER`
217            /// - `TypeCmp::Ne(TypeNe)`: if `VAL != OTHER`
218            ///
219            $($(#[$eq_docs])*)?
220            #[inline(always)]
221            pub const fn equals<const OTHER: $prim>(
222                self, 
223                _other: $struct<OTHER>,
224            ) -> crate::TypeCmp<$struct<VAL>, $struct<OTHER>> {
225                $crate::const_marker::Helper::<$struct<VAL>, $struct<OTHER>>::EQUALS
226            }
227        }
228
229        /////////
230
231        impl<const VAL: $prim> core::fmt::Debug for $struct<VAL> {
232            fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
233                core::fmt::Debug::fmt(&VAL, fmt)
234            }
235        }
236
237        impl<const L: $prim, const R: $prim> core::cmp::PartialEq<$struct<R>> for $struct<L> {
238            fn eq(&self, _: &$struct<R>) -> bool {
239                L == R
240            }
241        }
242
243        impl<const VAL: $prim> core::cmp::Eq for $struct<VAL> {}
244        
245        impl<const VAL: $prim> Default for $struct<VAL> {
246            fn default() -> Self {
247                Self
248            }
249        }
250
251        impl<const L: $prim, const R: $prim> core::cmp::PartialOrd<$struct<R>> for $struct<L> {
252            fn partial_cmp(&self, _: &$struct<R>) -> Option<core::cmp::Ordering> {
253                Some(core::cmp::Ord::cmp(&L, &R))
254            }
255        }
256
257        impl<const VAL: $prim> core::cmp::Ord for $struct<VAL> {
258            fn cmp(&self, _: &Self) -> core::cmp::Ordering {
259                core::cmp::Ordering::Equal
260            }
261        }
262
263        impl<const VAL: $prim> core::hash::Hash for $struct<VAL> {
264            fn hash<H>(&self, state: &mut H)
265            where
266                H: core::hash::Hasher,
267            {
268                core::hash::Hash::hash(&VAL, state)
269            }
270        }
271
272        #[cfg(feature = "serde")]
273        crate::const_marker::__declare_const_marker_serde_impls!{$struct($prim) $deser_type}
274
275
276    };
277} pub(crate) use __declare_const_param_type;
278
279
280declare_const_param_type!{
281    Bool(bool) primitive,
282
283    /// 
284    /// For getting a type witness that
285    /// `Bool<B>` is either `Bool<true>` or `Bool<false>`,
286    /// you can use [`BoolWit`].
287
288
289    /// 
290    fn equals;
291}
292declare_const_param_type!{Char(char) primitive,}
293
294declare_const_param_type!{U8(u8) int,}
295declare_const_param_type!{U16(u16) int,}
296declare_const_param_type!{U32(u32) int,}
297declare_const_param_type!{U64(u64) int,}
298declare_const_param_type!{U128(u128) int,}
299
300declare_const_param_type!{
301    Usize(usize)
302    int,
303
304    /// # Examples
305    /// 
306    /// ### Array
307    /// 
308    /// This example demonstrates how `Usize` can be used to 
309    /// specialize behavior on array length.
310    /// 
311    /// (this example requires Rust 1.61.0, because it uses trait bounds in const fns)
312    #[cfg_attr(not(feature = "rust_1_61"), doc = "```ignore")]
313    #[cfg_attr(feature = "rust_1_61", doc = "```rust")]
314    /// use typewit::{const_marker::Usize, TypeCmp, TypeEq};
315    /// 
316    /// assert_eq!(try_from_pair::<_, 0>((3, 5)), Ok([]));
317    /// assert_eq!(try_from_pair::<_, 1>((3, 5)), Ok([3]));
318    /// assert_eq!(try_from_pair::<_, 2>((3, 5)), Ok([3, 5]));
319    /// assert_eq!(try_from_pair::<_, 3>((3, 5)), Err((3, 5)));
320    /// 
321    /// 
322    /// const fn try_from_pair<T: Copy, const LEN: usize>(pair: (T, T)) -> Result<[T; LEN], (T, T)> {
323    ///     if let TypeCmp::Eq(te_len) = Usize::<LEN>.equals(Usize::<0>) {
324    ///         // this branch is ran on `LEN == 0`
325    ///         // `te_len` is a `TypeEq<Usize<LEN>, Usize<0>>`
326    ///         Ok(
327    ///             TypeEq::new::<T>()    // `TypeEq<T, T>`
328    ///                 .in_array(te_len) // `TypeEq<[T; LEN], [T; 0]>`
329    ///                 .to_left([])      // Goes from `[T; 0]` to `[T; LEN]`
330    ///         )
331    ///     } else if let TypeCmp::Eq(te_len) = Usize.equals(Usize) {
332    ///         // this branch is ran on `LEN == 1`
333    ///         // `te_len` is inferred to be `TypeEq<Usize<LEN>, Usize<1>>`
334    ///         Ok(TypeEq::NEW.in_array(te_len).to_left([pair.0]))
335    ///     } else if let TypeCmp::Eq(te_len) = Usize.equals(Usize) {
336    ///         // this branch is ran on `LEN == 2`
337    ///         // `te_len` is inferred to be `TypeEq<Usize<LEN>, Usize<2>>`
338    ///         Ok(TypeEq::NEW.in_array(te_len).to_left([pair.0, pair.1]))
339    ///     } else {
340    ///         Err(pair)
341    ///     }
342    /// }
343    /// 
344    /// ```
345    /// 
346    /// ### Struct
347    /// 
348    /// This example demonstrates how `Usize` can be used to pass a 
349    /// const-generic struct to a function expecting a concrete type of that struct.
350    /// 
351    /// ```rust
352    /// use typewit::{const_marker::Usize, TypeCmp};
353    /// 
354    /// assert_eq!(mutate(Array([])), Array([]));
355    /// assert_eq!(mutate(Array([3])), Array([3]));
356    /// assert_eq!(mutate(Array([3, 5])), Array([3, 5]));
357    /// assert_eq!(mutate(Array([3, 5, 8])), Array([8, 5, 3])); // reversed!
358    /// assert_eq!(mutate(Array([3, 5, 8, 13])), Array([3, 5, 8, 13]));
359    /// 
360    /// 
361    /// #[derive(Debug, PartialEq)]
362    /// struct Array<const CAP: usize>([u32; CAP]);
363    /// 
364    /// const fn mutate<const LEN: usize>(arr: Array<LEN>) -> Array<LEN> {
365    ///     match Usize::<LEN>.equals(Usize::<3>) {
366    ///         // `te_len` is a `TypeEq<Usize<LEN>, Usize<3>>`
367    ///         // this branch is ran on `LEN == 3`
368    ///         TypeCmp::Eq(te_len) => {
369    ///             // `te` is a `TypeEq<Array<LEN>, Array<3>>`
370    ///             let te = te_len.project::<GArray>();
371    /// 
372    ///             // `te.to_right(...)` here goes from `Array<LEN>` to `Array<3>`
373    ///             let ret = reverse3(te.to_right(arr));
374    /// 
375    ///             // `te.to_left(...)` here goes from `Array<3>` to `Array<LEN>`
376    ///             te.to_left(ret)
377    ///         }
378    ///         TypeCmp::Ne(_) => arr,
379    ///     }
380    /// }
381    /// 
382    /// const fn reverse3(Array([a, b, c]): Array<3>) -> Array<3> {
383    ///     Array([c, b, a])
384    /// }
385    /// 
386    /// typewit::type_fn!{
387    ///     // Type-level function from `Usize<LEN>` to `Array<LEN>`
388    ///     struct GArray;
389    /// 
390    ///     impl<const LEN: usize> Usize<LEN> => Array<LEN>
391    /// }
392    /// ```
393    fn equals;
394}
395
396declare_const_param_type!{I8(i8) int,}
397declare_const_param_type!{I16(i16) int,}
398declare_const_param_type!{I32(i32) int,}
399declare_const_param_type!{I64(i64) int,}
400declare_const_param_type!{I128(i128) int,}
401declare_const_param_type!{Isize(isize) int,}
402
403
404
405
406
407
408
409
410
411
412
413
414macro_rules! __const_marker_deserialize_doc_example {
415    (int $struct:ident $example_ty:literal) => {concat!(
416        "```", $example_ty, "\n",
417        "use typewit::const_marker::", stringify!($struct), "; \n",
418        " \n",
419        "assert_eq!(serde_json::from_str::<",
420        stringify!($struct), 
421        "<1>>(\"1\").unwrap(), ",
422        stringify!($struct),
423        "::<1>); \n",
424        " \n",
425        "// trying to deserialize `", stringify!($struct),
426        "<1>` from any value other than `1` produces an error \n",
427        "assert!(serde_json::from_str::<", stringify!($struct), "<1>>(\"0\").is_err()); \n",
428        " \n",
429        " \n",
430        "assert_eq!(serde_json::to_string(&", stringify!($struct), "::<1>).unwrap(), \"1\"); \n",
431        " \n",
432        "assert_eq!(serde_json::to_string(&", stringify!($struct), "::<92>).unwrap(), \"92\"); \n",
433        " \n",
434        "```"
435    )};
436    ($other:ident $struct:ident $example_ty:tt) => { "" };
437}
438
439pub(crate) use __const_marker_deserialize_doc_example;