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#[cfg(feature = "rust_1_83")]
46mod const_marker_eq_traits;
47
48#[cfg(feature = "rust_1_83")]
49pub use const_marker_eq_traits::*;
50
51mod boolwit;
52
53pub use boolwit::*;
54
55
56#[cfg(feature = "adt_const_marker")]
57mod slice_const_markers;
58
59#[cfg(feature = "adt_const_marker")]
60#[cfg_attr(feature = "docsrs", doc(cfg(feature = "adt_const_marker")))]
61pub use slice_const_markers::Str;
62
63/// Marker types for `const FOO: &'static [T]` parameters.
64#[cfg(feature = "adt_const_marker")]
65#[cfg_attr(feature = "docsrs", doc(cfg(feature = "adt_const_marker")))]
66pub mod slice {
67    pub use super::slice_const_markers::{
68        BoolSlice,
69        CharSlice,
70        U8Slice,
71        U16Slice,
72        U32Slice,
73        U64Slice,
74        U128Slice,
75        UsizeSlice,
76        I8Slice,
77        I16Slice,
78        I32Slice,
79        I64Slice,
80        I128Slice,
81        IsizeSlice,
82        StrSlice,
83    };
84}
85
86struct Helper<L, R>(L, R);
87
88
89macro_rules! __const_eq_with {
90    ($L:expr, $R:expr) => {
91        $L == $R
92    };
93    ($L:expr, $R:expr, ($L2:ident, $R2:ident) $cmp:expr) => ({
94        let $L2 = $L;
95        let $R2 = $R;
96        $cmp
97    });
98} pub(crate) use __const_eq_with;
99
100
101macro_rules! declare_const_param_type {
102    ($($params:tt)*) => {
103        $crate::const_marker::__declare_const_param_type!{ $($params)* }
104
105        #[cfg(feature = "rust_1_83")]
106        $crate::const_marker::__const_marker_impls!{ $($params)* }
107    }
108} pub(crate) use declare_const_param_type;
109
110
111macro_rules! __declare_const_param_type {
112    (
113        $(#[$struct_docs:meta])*
114        $struct:ident($prim:ty)
115
116        $(
117            $(#[$eq_docs:meta])*
118            fn equals $(($L:ident, $R:ident) $comparator:block)?;
119        )?
120    ) => {
121        #[doc = concat!(
122            "Marker type for passing `const VAL: ", stringify!($prim),
123            "` as a type parameter."
124        )]
125        $(#[$struct_docs])*
126        #[derive(Copy, Clone)]
127        pub struct $struct<const VAL: $prim>;
128
129        impl<const VAL: $prim> crate::const_marker::ConstMarker for $struct<VAL> {
130            const VAL: Self::Of = VAL;
131            type Of = $prim;
132        }
133
134        impl<const L: $prim, const R: $prim> $crate::const_marker::Helper<$struct<L>, $struct<R>> {
135            const EQ: Result<
136                TypeEq<$struct<L>, $struct<R>>,
137                TypeNe<$struct<L>, $struct<R>>,
138            > = if crate::const_marker::__const_eq_with!(
139                L,
140                R
141                $($(, ($L, $R) $comparator)?)?
142            ) {
143                // SAFETY: `L == R` (both are std types with sensible Eq impls)
144                // therefore `$struct<L> == $struct<R>`
145                unsafe {
146                    Ok(TypeEq::<$struct<L>, $struct<R>>::new_unchecked())
147                }
148            } else {
149                // SAFETY: `L != R` (both are std types with sensible Eq impls)
150                // therefore `$struct<L> != $struct<R>`
151                unsafe {
152                    Err(TypeNe::<$struct<L>, $struct<R>>::new_unchecked())
153                }
154            };
155
156            const EQUALS: crate::TypeCmp<$struct<L>, $struct<R>> = match Self::EQ {
157                Ok(x) => crate::TypeCmp::Eq(x),
158                Err(x) => crate::TypeCmp::Ne(x),
159            };
160        }
161
162        impl<const VAL: $prim> $struct<VAL> {
163            /// Compares `self` and `other` for equality.
164            ///
165            /// Returns:
166            /// - `Ok(TypeEq)`: if `VAL == OTHER`
167            /// - `Err(TypeNe)`: if `VAL != OTHER`
168            ///
169            #[inline(always)]
170            #[deprecated(note = "superceeded by `equals` method", since = "1.8.0")]
171            pub const fn eq<const OTHER: $prim>(
172                self, 
173                _other: $struct<OTHER>,
174            ) -> Result<
175                TypeEq<$struct<VAL>, $struct<OTHER>>,
176                TypeNe<$struct<VAL>, $struct<OTHER>>,
177            > {
178                $crate::const_marker::Helper::<$struct<VAL>, $struct<OTHER>>::EQ
179            }
180
181            /// Compares `self` and `other` for equality.
182            ///
183            /// Returns:
184            /// - `TypeCmp::Eq(TypeEq)`: if `VAL == OTHER`
185            /// - `TypeCmp::Ne(TypeNe)`: if `VAL != OTHER`
186            ///
187            $($(#[$eq_docs])*)?
188            #[inline(always)]
189            pub const fn equals<const OTHER: $prim>(
190                self, 
191                _other: $struct<OTHER>,
192            ) -> crate::TypeCmp<$struct<VAL>, $struct<OTHER>> {
193                $crate::const_marker::Helper::<$struct<VAL>, $struct<OTHER>>::EQUALS
194            }
195        }
196
197        /////////
198
199        impl<const VAL: $prim> core::fmt::Debug for $struct<VAL> {
200            fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
201                core::fmt::Debug::fmt(&VAL, fmt)
202            }
203        }
204
205        impl<const L: $prim, const R: $prim> core::cmp::PartialEq<$struct<R>> for $struct<L> {
206            fn eq(&self, _: &$struct<R>) -> bool {
207                L == R
208            }
209        }
210
211        impl<const VAL: $prim> core::cmp::Eq for $struct<VAL> {}
212
213    };
214} pub(crate) use __declare_const_param_type;
215
216
217declare_const_param_type!{
218    Bool(bool)
219
220    /// 
221    /// For getting a type witness that
222    /// `Bool<B>` is either `Bool<true>` or `Bool<false>`,
223    /// you can use [`BoolWit`].
224
225
226    /// 
227    fn equals;
228}
229declare_const_param_type!{Char(char)}
230
231declare_const_param_type!{U8(u8)}
232declare_const_param_type!{U16(u16)}
233declare_const_param_type!{U32(u32)}
234declare_const_param_type!{U64(u64)}
235declare_const_param_type!{U128(u128)}
236
237declare_const_param_type!{
238    Usize(usize)
239
240    /// # Examples
241    /// 
242    /// ### Array
243    /// 
244    /// This example demonstrates how `Usize` can be used to 
245    /// specialize behavior on array length.
246    /// 
247    /// (this example requires Rust 1.61.0, because it uses trait bounds in const fns)
248    #[cfg_attr(not(feature = "rust_1_61"), doc = "```ignore")]
249    #[cfg_attr(feature = "rust_1_61", doc = "```rust")]
250    /// use typewit::{const_marker::Usize, TypeCmp, TypeEq};
251    /// 
252    /// assert_eq!(try_from_pair::<_, 0>((3, 5)), Ok([]));
253    /// assert_eq!(try_from_pair::<_, 1>((3, 5)), Ok([3]));
254    /// assert_eq!(try_from_pair::<_, 2>((3, 5)), Ok([3, 5]));
255    /// assert_eq!(try_from_pair::<_, 3>((3, 5)), Err((3, 5)));
256    /// 
257    /// 
258    /// const fn try_from_pair<T: Copy, const LEN: usize>(pair: (T, T)) -> Result<[T; LEN], (T, T)> {
259    ///     if let TypeCmp::Eq(te_len) = Usize::<LEN>.equals(Usize::<0>) {
260    ///         // this branch is ran on `LEN == 0`
261    ///         // `te_len` is a `TypeEq<Usize<LEN>, Usize<0>>`
262    ///         Ok(
263    ///             TypeEq::new::<T>()    // `TypeEq<T, T>`
264    ///                 .in_array(te_len) // `TypeEq<[T; LEN], [T; 0]>`
265    ///                 .to_left([])      // Goes from `[T; 0]` to `[T; LEN]`
266    ///         )
267    ///     } else if let TypeCmp::Eq(te_len) = Usize.equals(Usize) {
268    ///         // this branch is ran on `LEN == 1`
269    ///         // `te_len` is inferred to be `TypeEq<Usize<LEN>, Usize<1>>`
270    ///         Ok(TypeEq::NEW.in_array(te_len).to_left([pair.0]))
271    ///     } else if let TypeCmp::Eq(te_len) = Usize.equals(Usize) {
272    ///         // this branch is ran on `LEN == 2`
273    ///         // `te_len` is inferred to be `TypeEq<Usize<LEN>, Usize<2>>`
274    ///         Ok(TypeEq::NEW.in_array(te_len).to_left([pair.0, pair.1]))
275    ///     } else {
276    ///         Err(pair)
277    ///     }
278    /// }
279    /// 
280    /// ```
281    /// 
282    /// ### Struct
283    /// 
284    /// This example demonstrates how `Usize` can be used to pass a 
285    /// const-generic struct to a function expecting a concrete type of that struct.
286    /// 
287    /// ```rust
288    /// use typewit::{const_marker::Usize, TypeCmp};
289    /// 
290    /// assert_eq!(mutate(Array([])), Array([]));
291    /// assert_eq!(mutate(Array([3])), Array([3]));
292    /// assert_eq!(mutate(Array([3, 5])), Array([3, 5]));
293    /// assert_eq!(mutate(Array([3, 5, 8])), Array([8, 5, 3])); // reversed!
294    /// assert_eq!(mutate(Array([3, 5, 8, 13])), Array([3, 5, 8, 13]));
295    /// 
296    /// 
297    /// #[derive(Debug, PartialEq)]
298    /// struct Array<const CAP: usize>([u32; CAP]);
299    /// 
300    /// const fn mutate<const LEN: usize>(arr: Array<LEN>) -> Array<LEN> {
301    ///     match Usize::<LEN>.equals(Usize::<3>) {
302    ///         // `te_len` is a `TypeEq<Usize<LEN>, Usize<3>>`
303    ///         // this branch is ran on `LEN == 3`
304    ///         TypeCmp::Eq(te_len) => {
305    ///             // `te` is a `TypeEq<Array<LEN>, Array<3>>`
306    ///             let te = te_len.project::<GArray>();
307    /// 
308    ///             // `te.to_right(...)` here goes from `Array<LEN>` to `Array<3>`
309    ///             let ret = reverse3(te.to_right(arr));
310    /// 
311    ///             // `te.to_left(...)` here goes from `Array<3>` to `Array<LEN>`
312    ///             te.to_left(ret)
313    ///         }
314    ///         TypeCmp::Ne(_) => arr,
315    ///     }
316    /// }
317    /// 
318    /// const fn reverse3(Array([a, b, c]): Array<3>) -> Array<3> {
319    ///     Array([c, b, a])
320    /// }
321    /// 
322    /// typewit::type_fn!{
323    ///     // Type-level function from `Usize<LEN>` to `Array<LEN>`
324    ///     struct GArray;
325    /// 
326    ///     impl<const LEN: usize> Usize<LEN> => Array<LEN>
327    /// }
328    /// ```
329    fn equals;
330}
331
332declare_const_param_type!{I8(i8)}
333declare_const_param_type!{I16(i16)}
334declare_const_param_type!{I32(i32)}
335declare_const_param_type!{I64(i64)}
336declare_const_param_type!{I128(i128)}
337declare_const_param_type!{Isize(isize)}
338