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 $(#[$struct_docs])*
140 #[derive(Copy, Clone)]
141 pub struct $struct<const VAL: $prim>;
142
143 impl<const VAL: $prim> crate::const_marker::ConstMarker for $struct<VAL> {
144 const VAL: Self::Of = VAL;
145 type Of = $prim;
146 }
147
148 impl<const L: $prim, const R: $prim> $crate::const_marker::Helper<$struct<L>, $struct<R>> {
149 const EQ: Result<
150 TypeEq<$struct<L>, $struct<R>>,
151 TypeNe<$struct<L>, $struct<R>>,
152 > = if crate::const_marker::__const_eq_with!(
153 L,
154 R
155 $($(, ($L, $R) $comparator)?)?
156 ) {
157 // SAFETY: `L == R` (both are std types with sensible Eq impls)
158 // therefore `$struct<L> == $struct<R>`
159 unsafe {
160 Ok(TypeEq::<$struct<L>, $struct<R>>::new_unchecked())
161 }
162 } else {
163 // SAFETY: `L != R` (both are std types with sensible Eq impls)
164 // therefore `$struct<L> != $struct<R>`
165 unsafe {
166 Err(TypeNe::<$struct<L>, $struct<R>>::new_unchecked())
167 }
168 };
169
170 const EQUALS: crate::TypeCmp<$struct<L>, $struct<R>> = match Self::EQ {
171 Ok(x) => crate::TypeCmp::Eq(x),
172 Err(x) => crate::TypeCmp::Ne(x),
173 };
174 }
175
176 impl<const VAL: $prim> $struct<VAL> {
177 /// Compares `self` and `other` for equality.
178 ///
179 /// Returns:
180 /// - `Ok(TypeEq)`: if `VAL == OTHER`
181 /// - `Err(TypeNe)`: if `VAL != OTHER`
182 ///
183 #[inline(always)]
184 #[deprecated(note = "superceeded by `equals` method", since = "1.8.0")]
185 pub const fn eq<const OTHER: $prim>(
186 self,
187 _other: $struct<OTHER>,
188 ) -> Result<
189 TypeEq<$struct<VAL>, $struct<OTHER>>,
190 TypeNe<$struct<VAL>, $struct<OTHER>>,
191 > {
192 $crate::const_marker::Helper::<$struct<VAL>, $struct<OTHER>>::EQ
193 }
194
195 /// Compares `self` and `other` for equality.
196 ///
197 /// Returns:
198 /// - `TypeCmp::Eq(TypeEq)`: if `VAL == OTHER`
199 /// - `TypeCmp::Ne(TypeNe)`: if `VAL != OTHER`
200 ///
201 $($(#[$eq_docs])*)?
202 #[inline(always)]
203 pub const fn equals<const OTHER: $prim>(
204 self,
205 _other: $struct<OTHER>,
206 ) -> crate::TypeCmp<$struct<VAL>, $struct<OTHER>> {
207 $crate::const_marker::Helper::<$struct<VAL>, $struct<OTHER>>::EQUALS
208 }
209 }
210
211 /////////
212
213 impl<const VAL: $prim> core::fmt::Debug for $struct<VAL> {
214 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
215 core::fmt::Debug::fmt(&VAL, fmt)
216 }
217 }
218
219 impl<const L: $prim, const R: $prim> core::cmp::PartialEq<$struct<R>> for $struct<L> {
220 fn eq(&self, _: &$struct<R>) -> bool {
221 L == R
222 }
223 }
224
225 impl<const VAL: $prim> core::cmp::Eq for $struct<VAL> {}
226
227 impl<const VAL: $prim> Default for $struct<VAL> {
228 fn default() -> Self {
229 Self
230 }
231 }
232
233 impl<const L: $prim, const R: $prim> core::cmp::PartialOrd<$struct<R>> for $struct<L> {
234 fn partial_cmp(&self, _: &$struct<R>) -> Option<core::cmp::Ordering> {
235 Some(core::cmp::Ord::cmp(&L, &R))
236 }
237 }
238
239 impl<const VAL: $prim> core::cmp::Ord for $struct<VAL> {
240 fn cmp(&self, _: &Self) -> core::cmp::Ordering {
241 core::cmp::Ordering::Equal
242 }
243 }
244
245 impl<const VAL: $prim> core::hash::Hash for $struct<VAL> {
246 fn hash<H>(&self, state: &mut H)
247 where
248 H: core::hash::Hasher,
249 {
250 core::hash::Hash::hash(&VAL, state)
251 }
252 }
253
254 #[cfg(feature = "serde")]
255 crate::const_marker::__declare_const_marker_serde_impls!{$struct($prim) $deser_type}
256
257
258 };
259} pub(crate) use __declare_const_param_type;
260
261
262declare_const_param_type!{
263 Bool(bool) primitive,
264
265 ///
266 /// For getting a type witness that
267 /// `Bool<B>` is either `Bool<true>` or `Bool<false>`,
268 /// you can use [`BoolWit`].
269
270
271 ///
272 fn equals;
273}
274declare_const_param_type!{Char(char) primitive,}
275
276declare_const_param_type!{U8(u8) primitive,}
277declare_const_param_type!{U16(u16) primitive,}
278declare_const_param_type!{U32(u32) primitive,}
279declare_const_param_type!{U64(u64) primitive,}
280declare_const_param_type!{U128(u128) primitive,}
281
282declare_const_param_type!{
283 Usize(usize)
284 primitive,
285
286 /// # Examples
287 ///
288 /// ### Array
289 ///
290 /// This example demonstrates how `Usize` can be used to
291 /// specialize behavior on array length.
292 ///
293 /// (this example requires Rust 1.61.0, because it uses trait bounds in const fns)
294 #[cfg_attr(not(feature = "rust_1_61"), doc = "```ignore")]
295 #[cfg_attr(feature = "rust_1_61", doc = "```rust")]
296 /// use typewit::{const_marker::Usize, TypeCmp, TypeEq};
297 ///
298 /// assert_eq!(try_from_pair::<_, 0>((3, 5)), Ok([]));
299 /// assert_eq!(try_from_pair::<_, 1>((3, 5)), Ok([3]));
300 /// assert_eq!(try_from_pair::<_, 2>((3, 5)), Ok([3, 5]));
301 /// assert_eq!(try_from_pair::<_, 3>((3, 5)), Err((3, 5)));
302 ///
303 ///
304 /// const fn try_from_pair<T: Copy, const LEN: usize>(pair: (T, T)) -> Result<[T; LEN], (T, T)> {
305 /// if let TypeCmp::Eq(te_len) = Usize::<LEN>.equals(Usize::<0>) {
306 /// // this branch is ran on `LEN == 0`
307 /// // `te_len` is a `TypeEq<Usize<LEN>, Usize<0>>`
308 /// Ok(
309 /// TypeEq::new::<T>() // `TypeEq<T, T>`
310 /// .in_array(te_len) // `TypeEq<[T; LEN], [T; 0]>`
311 /// .to_left([]) // Goes from `[T; 0]` to `[T; LEN]`
312 /// )
313 /// } else if let TypeCmp::Eq(te_len) = Usize.equals(Usize) {
314 /// // this branch is ran on `LEN == 1`
315 /// // `te_len` is inferred to be `TypeEq<Usize<LEN>, Usize<1>>`
316 /// Ok(TypeEq::NEW.in_array(te_len).to_left([pair.0]))
317 /// } else if let TypeCmp::Eq(te_len) = Usize.equals(Usize) {
318 /// // this branch is ran on `LEN == 2`
319 /// // `te_len` is inferred to be `TypeEq<Usize<LEN>, Usize<2>>`
320 /// Ok(TypeEq::NEW.in_array(te_len).to_left([pair.0, pair.1]))
321 /// } else {
322 /// Err(pair)
323 /// }
324 /// }
325 ///
326 /// ```
327 ///
328 /// ### Struct
329 ///
330 /// This example demonstrates how `Usize` can be used to pass a
331 /// const-generic struct to a function expecting a concrete type of that struct.
332 ///
333 /// ```rust
334 /// use typewit::{const_marker::Usize, TypeCmp};
335 ///
336 /// assert_eq!(mutate(Array([])), Array([]));
337 /// assert_eq!(mutate(Array([3])), Array([3]));
338 /// assert_eq!(mutate(Array([3, 5])), Array([3, 5]));
339 /// assert_eq!(mutate(Array([3, 5, 8])), Array([8, 5, 3])); // reversed!
340 /// assert_eq!(mutate(Array([3, 5, 8, 13])), Array([3, 5, 8, 13]));
341 ///
342 ///
343 /// #[derive(Debug, PartialEq)]
344 /// struct Array<const CAP: usize>([u32; CAP]);
345 ///
346 /// const fn mutate<const LEN: usize>(arr: Array<LEN>) -> Array<LEN> {
347 /// match Usize::<LEN>.equals(Usize::<3>) {
348 /// // `te_len` is a `TypeEq<Usize<LEN>, Usize<3>>`
349 /// // this branch is ran on `LEN == 3`
350 /// TypeCmp::Eq(te_len) => {
351 /// // `te` is a `TypeEq<Array<LEN>, Array<3>>`
352 /// let te = te_len.project::<GArray>();
353 ///
354 /// // `te.to_right(...)` here goes from `Array<LEN>` to `Array<3>`
355 /// let ret = reverse3(te.to_right(arr));
356 ///
357 /// // `te.to_left(...)` here goes from `Array<3>` to `Array<LEN>`
358 /// te.to_left(ret)
359 /// }
360 /// TypeCmp::Ne(_) => arr,
361 /// }
362 /// }
363 ///
364 /// const fn reverse3(Array([a, b, c]): Array<3>) -> Array<3> {
365 /// Array([c, b, a])
366 /// }
367 ///
368 /// typewit::type_fn!{
369 /// // Type-level function from `Usize<LEN>` to `Array<LEN>`
370 /// struct GArray;
371 ///
372 /// impl<const LEN: usize> Usize<LEN> => Array<LEN>
373 /// }
374 /// ```
375 fn equals;
376}
377
378declare_const_param_type!{I8(i8) primitive,}
379declare_const_param_type!{I16(i16) primitive,}
380declare_const_param_type!{I32(i32) primitive,}
381declare_const_param_type!{I64(i64) primitive,}
382declare_const_param_type!{I128(i128) primitive,}
383declare_const_param_type!{Isize(isize) primitive,}
384