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