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