try_specialize/
specialization.rs

1use core::cmp::Ordering;
2use core::fmt::{Debug, Formatter, Result as FmtResult};
3use core::hash::{Hash, Hasher};
4use core::marker::PhantomData;
5use core::mem::{transmute_copy, ManuallyDrop};
6
7use crate::{static_type_eq, type_eq, type_eq_ignore_lifetimes, LifetimeFree, TypeFn};
8
9/// A zero-sized marker struct that guarantees type equality between `T1` and
10/// `T2`.
11///
12/// This struct provides a type-safe mechanism to specialize one type into
13/// another, enforcing that `T1` and `T2` are identical concrete types. It can
14/// only be instantiated when both types are the same.
15///
16/// `Specialization` trait refers to a lower-level API. Prefer to use
17/// [`TrySpecialize`] trait methods if applicable.
18///
19/// Library tests ensure that the specializations are performed at compile time
20/// and are fully optimized with no runtime cost at `opt-level >= 1`. Note that
21/// the release profile uses `opt-level = 3` by default.
22///
23/// Constructors cheat sheet:
24/// | Type bounds | Constructor |
25/// |:--:|:--:|
26/// | `T1: 'static + LifetimeFree` | [`try_new`] |
27/// | `T2: 'static + LifetimeFree` | [`try_new`] + [`rev`] |
28/// | `T1: 'static, T2: 'static` | [`try_new_static`] |
29/// | underlying type maybe impls `LifetimeFree` | [`try_new_if_lifetime_free_weak`] |
30/// | `unsafe`, without lifetimes check | [`try_new_ignore_lifetimes`] |
31///
32/// Specialization methods cheat sheet:
33/// | From | To | Method |
34/// |:--:|:--:|:--:|
35/// | `T1` | `T2` | [`specialize`] |
36/// | `&T1` | `&T2` | [`specialize_ref`] |
37/// | `&mut T1` | `&mut T2` | [`specialize_mut`] |
38/// | `T2` | `T1` | [`rev`] + [`specialize`] |
39/// | `Wrapper<T1>` | `Wrapper<T2>` | [`map`] + [`specialize`] |
40/// | `T1::AssociatedType` | `T2::AssociatedType` | [`map`] + [`specialize`] |
41///
42/// [`TrySpecialize`]: crate::TrySpecialize
43/// [`try_new`]: Specialization::try_new
44/// [`rev`]: Specialization::rev
45/// [`map`]: Specialization::map
46/// [`try_new_static`]: Specialization::try_new_static
47/// [`try_new_if_lifetime_free_weak`]: crate::unreliable::WeakSpecialization::try_new_if_lifetime_free_weak
48/// [`try_new_ignore_lifetimes`]: Specialization::try_new_ignore_lifetimes
49/// [`specialize`]: Specialization::specialize
50/// [`specialize_ref`]: Specialization::specialize_ref
51/// [`specialize_mut`]: Specialization::specialize_mut
52pub struct Specialization<T1, T2>(PhantomData<T1>, PhantomData<T2>)
53where
54    T1: ?Sized,
55    T2: ?Sized;
56
57impl<T1, T2> Specialization<T1, T2>
58where
59    T1: ?Sized,
60    T2: ?Sized,
61{
62    /// Checks the types `T1` and `T2` for equality and returns the
63    /// specialization provider if types are equal.
64    ///
65    /// Note that this method requires source type to implement
66    /// [`LifetimeFree`].
67    /// Use `Specialization::try_new().rev()` method to check the target
68    /// type instead.
69    /// The [`LifetimeFree`] trait is **not** automatically derived for all
70    /// lifetime-free types. The library only implements it for standard library
71    /// types that do not have any lifetime parameters.
72    ///
73    /// For simple cases consider using [`TrySpecialize`] methods like
74    /// [`try_specialize`],
75    /// [`try_specialize_ref`], and
76    /// [`try_specialize_mut`] instead.
77    /// You can use [`Specialization::try_new_static`] if both types are
78    /// `'static`.
79    ///
80    /// [`LifetimeFree`]: crate::LifetimeFree
81    /// [`TrySpecialize`]: crate::TrySpecialize
82    /// [`try_specialize`]: crate::TrySpecialize::try_specialize
83    /// [`try_specialize_ref`]: crate::TrySpecialize::try_specialize_ref
84    /// [`try_specialize_mut`]: crate::TrySpecialize::try_specialize_mut
85    ///
86    /// # Examples
87    ///
88    /// Same-type stringifiable type concatenation, that don't allocate memory
89    /// if one of the arguments is empty `&str`:
90    /// ```rust
91    /// use core::fmt::Display;
92    /// use std::borrow::Cow;
93    /// use std::format;
94    ///
95    /// use try_specialize::Specialization;
96    ///
97    /// fn concat_same<'a, T>(first: &'a T, second: &'a T) -> Cow<'a, str>
98    /// where
99    ///     T: ?Sized + Display,
100    /// {
101    ///     if let Some(spec) = Specialization::<T, str>::try_new() {
102    ///         match (spec.specialize_ref(first), spec.specialize_ref(second)) {
103    ///             (first, "") => Cow::Borrowed(first),
104    ///             ("", second) => Cow::Borrowed(second),
105    ///             (first, second) => Cow::Owned([first, second].concat()),
106    ///         }
107    ///     } else {
108    ///         Cow::Owned(format!("{first}{second}"))
109    ///     }
110    /// }
111    ///
112    /// assert!(matches!(concat_same("foo", "bar"), Cow::Owned(v) if v == "foobar"));
113    /// assert!(matches!(concat_same("foo", ""), Cow::Borrowed("foo")));
114    /// assert!(matches!(concat_same("", "bar"), Cow::Borrowed("bar")));
115    /// let foo = String::from("foo");
116    /// let bar = String::from("bar");
117    /// assert!(matches!(concat_same(&foo, &bar), Cow::Owned(v) if v == "foobar"));
118    /// assert!(matches!(concat_same(&123, &456), Cow::Owned(v) if v == "123456"));
119    /// ```
120    ///
121    /// Generate a placeholder with non-default value for some types:
122    /// ```rust
123    /// use try_specialize::Specialization;
124    ///
125    /// fn placeholder<T>() -> T
126    /// where
127    ///     T: Default,
128    /// {
129    ///     if let Some(spec) = Specialization::<T, u8>::try_new() {
130    ///         spec.rev().specialize(12)
131    ///     } else if let Some(spec) = Specialization::<T, u32>::try_new() {
132    ///         spec.rev().specialize(42)
133    ///     } else if let Some(spec) = Specialization::<T, f64>::try_new() {
134    ///         spec.rev().specialize(123.456)
135    ///     // ...
136    ///     } else {
137    ///         T::default()
138    ///     }
139    /// }
140    ///
141    /// assert_eq!(placeholder::<&'static str>(), "");
142    /// assert_eq!(placeholder::<u8>(), 12);
143    /// assert_eq!(placeholder::<(u8, u8)>(), (0, 0));
144    /// assert_eq!(placeholder::<u32>(), 42);
145    /// assert_eq!(placeholder::<f64>(), 123.456);
146    /// assert_eq!(placeholder::<i128>(), 0);
147    /// ```
148    #[inline]
149    #[must_use]
150    pub fn try_new() -> Option<Self>
151    where
152        T2: LifetimeFree,
153    {
154        // SAFETY: `T1` can be specialized to `T2` if the types are equal.
155        type_eq::<T1, T2>().then_some(unsafe { Self::new_unchecked() })
156    }
157
158    /// Checks the types `T1` and `T2` for equality and returns the
159    /// specialization provider if types are equal.
160    ///
161    /// Note that this method requires both types to have `'static` lifetime,
162    /// but don't require any type to implement `LifetimeFree`.
163    ///
164    /// For simple cases consider using [`TrySpecialize`] methods like
165    /// [`try_specialize_static`],
166    /// [`try_specialize_ref_static`], and
167    /// [`try_specialize_mut_static`] instead.
168    /// You can use [`Specialization::try_new`] if the target type
169    /// implements [`LifetimeFree`] trait or
170    /// `Specialization::try_new().rev()` if the source type implements
171    /// [`LifetimeFree`] trait.
172    ///
173    /// # Examples
174    ///
175    /// Collection reverse function with optimized specializations for `Vec<T>`
176    /// and `Box<[T]>`:
177    /// ```rust
178    /// use core::sync::atomic::{AtomicU32, Ordering as AtomicOrdering};
179    /// use std::collections::VecDeque;
180    ///
181    /// use try_specialize::Specialization;
182    ///
183    /// static DEBUG_SPEC_USED: AtomicU32 = AtomicU32::new(0);
184    ///
185    /// fn reverse_collection<T>(value: T) -> T
186    /// where
187    ///     T: 'static + IntoIterator + FromIterator<T::Item>,
188    ///     T::IntoIter: DoubleEndedIterator,
189    /// {
190    ///     let spec1 = Specialization::<T, Vec<T::Item>>::try_new_static();
191    ///     let spec2 = Specialization::<T, Box<[T::Item]>>::try_new_static();
192    ///
193    ///     if let Some(spec1) = spec1 {
194    ///         DEBUG_SPEC_USED.store(101, AtomicOrdering::Relaxed);
195    ///         let mut vec = spec1.specialize(value);
196    ///         vec.reverse();
197    ///         spec1.rev().specialize(vec)
198    ///     } else if let Some(spec2) = spec2 {
199    ///         DEBUG_SPEC_USED.store(202, AtomicOrdering::Relaxed);
200    ///         let mut boxed = spec2.specialize(value);
201    ///         boxed.reverse();
202    ///         spec2.rev().specialize(boxed)
203    ///     } else {
204    ///         DEBUG_SPEC_USED.store(303, AtomicOrdering::Relaxed);
205    ///         value.into_iter().rev().collect()
206    ///     }
207    /// }
208    ///
209    /// assert_eq!(reverse_collection(vec![1, 2, 3]), vec![3, 2, 1]);
210    /// assert_eq!(DEBUG_SPEC_USED.load(AtomicOrdering::Relaxed), 101);
211    ///
212    /// assert_eq!(
213    ///     reverse_collection(vec![1, 2, 3].into_boxed_slice()),
214    ///     vec![3, 2, 1].into_boxed_slice()
215    /// );
216    /// assert_eq!(DEBUG_SPEC_USED.load(AtomicOrdering::Relaxed), 202);
217    ///
218    /// assert_eq!(
219    ///     reverse_collection(VecDeque::from([1, 2, 3])),
220    ///     VecDeque::from([3, 2, 1])
221    /// );
222    /// assert_eq!(DEBUG_SPEC_USED.load(AtomicOrdering::Relaxed), 303);
223    /// ```
224    ///
225    /// Same-type stringifiable type concatenation, that don't allocate memory
226    /// if one of the arguments is empty `&'static str` and avoid reallocations
227    /// if one of the arguments is empty `String`:
228    /// ```rust
229    /// use core::fmt::Display;
230    /// use std::borrow::Cow;
231    ///
232    /// use try_specialize::Specialization;
233    ///
234    /// fn concat_same<T>(first: T, second: T) -> Cow<'static, str>
235    /// where
236    ///     T: 'static + Display,
237    /// {
238    ///     if let Some(spec) = Specialization::<T, &'static str>::try_new_static() {
239    ///         match (spec.specialize(first), spec.specialize(second)) {
240    ///             (first, "") => Cow::Borrowed(first),
241    ///             ("", second) => Cow::Borrowed(second),
242    ///             (first, second) => Cow::Owned([first, second].concat()),
243    ///         }
244    ///     } else if let Some(spec) = Specialization::<T, String>::try_new_static() {
245    ///         let first = spec.specialize(first);
246    ///         let second = spec.specialize(second);
247    ///         match (first.is_empty(), second.is_empty()) {
248    ///             (false | true, true) => Cow::Owned(first),
249    ///             (true, false) => Cow::Owned(second),
250    ///             (false, false) => Cow::Owned(first + &second),
251    ///         }
252    ///     } else {
253    ///         Cow::Owned(format!("{first}{second}"))
254    ///     }
255    /// }
256    ///
257    /// assert!(matches!(concat_same("foo", "bar"), Cow::Owned(v) if v == "foobar"));
258    /// assert!(matches!(concat_same("foo", ""), Cow::Borrowed("foo")));
259    /// assert!(matches!(concat_same("", "bar"), Cow::Borrowed("bar")));
260    /// let foo = String::from("foo");
261    /// let bar = String::from("bar");
262    /// assert!(matches!(concat_same(foo, bar), Cow::Owned(v) if v == "foobar"));
263    /// let empty = String::new();
264    /// let bar = String::from("bar");
265    /// assert!(matches!(concat_same(empty, bar), Cow::Owned(v) if v == "bar"));
266    /// let foo = String::from("foo");
267    /// let empty = String::new();
268    /// assert!(matches!(concat_same(foo, empty), Cow::Owned(v) if v == "foo"));
269    /// assert!(matches!(concat_same(123, 456), Cow::Owned(v) if v == "123456"));
270    /// ```
271    ///
272    /// Generate a placeholder with non-default value for some types:
273    /// ```rust
274    /// use try_specialize::Specialization;
275    ///
276    /// fn placeholder<T>() -> T
277    /// where
278    ///     T: 'static + Default,
279    /// {
280    ///     if let Some(spec) = Specialization::<T, &'static str>::try_new_static() {
281    ///         spec.rev().specialize("dummy string")
282    ///     } else if let Some(spec) = Specialization::<T, u32>::try_new() {
283    ///         spec.rev().specialize(42)
284    ///     } else if let Some(spec) = Specialization::<T, f64>::try_new() {
285    ///         spec.rev().specialize(123.456)
286    ///     // ...
287    ///     } else {
288    ///         T::default()
289    ///     }
290    /// }
291    ///
292    /// assert_eq!(placeholder::<&'static str>(), "dummy string");
293    /// assert_eq!(placeholder::<(u8, u8)>(), (0, 0));
294    /// assert_eq!(placeholder::<u32>(), 42);
295    /// assert_eq!(placeholder::<f64>(), 123.456);
296    /// assert_eq!(placeholder::<i128>(), 0);
297    /// ```
298    ///
299    /// [`TrySpecialize`]: crate::TrySpecialize
300    /// [`try_specialize_static`]: crate::TrySpecialize::try_specialize_static
301    /// [`try_specialize_ref_static`]: crate::TrySpecialize::try_specialize_ref_static
302    /// [`try_specialize_mut_static`]: crate::TrySpecialize::try_specialize_mut_static
303    #[inline]
304    #[must_use]
305    pub fn try_new_static() -> Option<Self>
306    where
307        T1: 'static,
308        T2: 'static,
309    {
310        // SAFETY: `T1` can be specialized to `T2` if the types are equal.
311        static_type_eq::<T1, T2>().then_some(unsafe { Self::new_unchecked() })
312    }
313
314    /// Checks the types `T1` and `T2` for equality and returns the
315    /// specialization provider if the types are equal ignoring lifetimes.
316    ///
317    /// For simple cases consider using [`TrySpecialize`] methods like
318    /// [`try_specialize_ignore_lifetimes`],
319    /// [`try_specialize_ref_ignore_lifetimes`], and
320    /// [`try_specialize_mut_ignore_lifetimes`] instead.
321    ///
322    /// # Safety
323    ///
324    /// This method doesn't validate type lifetimes. Lifetimes equality should
325    /// be validated separately.
326    ///
327    /// # Examples
328    ///
329    /// Specialized to third-party library item that can definitely be
330    /// `LifetimeFree`.
331    /// ```rust
332    /// mod third_party_lib {
333    ///     #[derive(Eq, PartialEq, Default, Debug)]
334    ///     pub struct Marker(pub u32);
335    /// }
336    ///
337    /// use third_party_lib::Marker;
338    /// use try_specialize::Specialization;
339    ///
340    /// fn inc_if_marker<T>(value: T) -> T {
341    ///     // SAFETY: `Marker` type has no lifetime parameters.
342    ///     if let Some(spec) = unsafe { Specialization::<T, Marker>::try_new_ignore_lifetimes() } {
343    ///         spec.rev().specialize(Marker(spec.specialize(value).0 + 1))
344    ///     } else {
345    ///         value
346    ///     }
347    /// }
348    ///
349    /// assert_eq!(inc_if_marker(123), 123);
350    /// assert_eq!(inc_if_marker("str"), "str");
351    /// assert_eq!(inc_if_marker(Marker(123)), Marker(124));
352    /// ```
353    ///
354    /// [`TrySpecialize`]: crate::TrySpecialize
355    /// [`try_specialize_ignore_lifetimes`]: crate::TrySpecialize::try_specialize_ignore_lifetimes
356    /// [`try_specialize_ref_ignore_lifetimes`]: crate::TrySpecialize::try_specialize_ref_ignore_lifetimes
357    /// [`try_specialize_mut_ignore_lifetimes`]: crate::TrySpecialize::try_specialize_mut_ignore_lifetimes
358    #[inline]
359    #[must_use]
360    pub unsafe fn try_new_ignore_lifetimes() -> Option<Self> {
361        // SAFETY: `T1` can be specialized to `T2` if the caller guarantees that
362        // type lifetimes are equal.
363        type_eq_ignore_lifetimes::<T1, T2>().then_some(unsafe { Self::new_unchecked() })
364    }
365
366    /// Construct a new `Specialization<T1, T2>` without any types
367    /// equality checks.
368    ///
369    /// # Safety
370    ///
371    /// Calling this method for `Specialization<T1, T2>` with different
372    /// types in `T1` and `T2` is *[undefined behavior]*.
373    ///
374    /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
375    #[inline]
376    #[must_use]
377    pub const unsafe fn new_unchecked() -> Self {
378        Self(PhantomData, PhantomData)
379    }
380
381    /// Reverses the specialization.
382    ///
383    /// It can be used to convert the target type back to source type and also
384    /// to create a specialization from a type which implements `LifetimeFree`,
385    /// for example: `Specialization::<u8, T>::new().rev()`.
386    ///
387    /// For simple cases consider using [`TrySpecialize`] methods like
388    /// [`try_specialize_from`],
389    /// [`try_specialize_from_ref`], and
390    /// [`try_specialize_from_mut`] instead.
391    ///
392    /// # Examples
393    ///
394    /// Synthetic example with custom behavior for `u32` type:
395    /// ```rust
396    /// use try_specialize::{LifetimeFree, Specialization};
397    ///
398    /// fn inc_if_u32<T>(value: T) -> T
399    /// where
400    ///     T: LifetimeFree,
401    /// {
402    ///     if let Some(spec) = Specialization::<T, u32>::try_new() {
403    ///         spec.rev().specialize(spec.specialize(value) + 1)
404    ///     } else {
405    ///         value
406    ///     }
407    /// }
408    ///
409    /// assert_eq!(inc_if_u32(123_u32), 124);
410    /// assert_eq!(inc_if_u32(123_i32), 123);
411    /// assert_eq!(inc_if_u32(123_u8), 123);
412    /// ```
413    ///
414    /// More realistic example can be found at
415    /// [`examples/encode.rs`](https://github.com/zheland/try-specialize/blob/v0.1.2/examples/encode.rs).
416    ///
417    /// [`TrySpecialize`]: crate::TrySpecialize
418    /// [`try_specialize_from`]: crate::TrySpecialize::try_specialize_from
419    /// [`try_specialize_from_ref`]: crate::TrySpecialize::try_specialize_from_ref
420    /// [`try_specialize_from_mut`]: crate::TrySpecialize::try_specialize_from_mut
421    #[inline]
422    #[must_use]
423    pub const fn rev(&self) -> Specialization<T2, T1> {
424        Specialization(PhantomData, PhantomData)
425    }
426
427    /// Maps the specialization types to other types using a specified
428    /// `TypeFn`.
429    ///
430    /// This can be useful to specialize third-party wrappers or the trait
431    /// associated types if they are based on `LifetimeFree` types.
432    ///
433    /// # Examples
434    ///
435    /// Simplified custom vec-like type processing optimization example:
436    /// ```rust
437    /// mod third_party_lib {
438    ///     pub struct CustomVec<T> {
439    /// #       pub inner: Vec<T>
440    ///         // ...
441    ///     }
442    /// }
443    ///
444    /// use third_party_lib::CustomVec;
445    /// use try_specialize::{Specialization, TypeFn};
446    ///
447    /// fn process<T>(data: CustomVec<T>) {
448    ///     struct MapIntoCustomVec;
449    ///     impl<T> TypeFn<T> for MapIntoCustomVec {
450    ///         type Output = CustomVec<T>;
451    ///     }
452    ///
453    ///     if let Some(spec) = Specialization::<T, u8>::try_new() {
454    ///         let data: CustomVec<u8> = spec.map::<MapIntoCustomVec>().specialize(data);
455    ///         // optimized specialized implementation...
456    ///     } else {
457    ///         // default implementation...
458    ///     }
459    /// }
460    /// # process(CustomVec { inner: vec![1_u8, 2, 3] });
461    /// # process(CustomVec { inner: vec![1_i8, 2, 3] });
462    /// ```
463    ///
464    /// Simplified custom `hashbrown::HashMap` type processing optimization
465    /// example with multiple type generics:
466    /// ```rust
467    /// use hashbrown::HashMap;
468    /// use try_specialize::{Specialization, TypeFn};
469    ///
470    /// fn process<K, V>(data: HashMap<K, V>)
471    /// where
472    ///     K: 'static,
473    ///     V: 'static,
474    /// {
475    ///     struct MapIntoHashMap;
476    ///     impl<K, V> TypeFn<(K, V)> for MapIntoHashMap {
477    ///         type Output = HashMap<K, V>;
478    ///     }
479    ///
480    ///     if let Some(spec) = Specialization::<(K, V), (char, char)>::try_new_static() {
481    ///         let data: HashMap<char, char> = spec.map::<MapIntoHashMap>().specialize(data);
482    ///         // optimized specialized implementation...
483    ///     } else if let Some(spec) = Specialization::<(K, V), (String, String)>::try_new_static() {
484    ///         let data: HashMap<String, String> = spec.map::<MapIntoHashMap>().specialize(data);
485    ///         // optimized specialized implementation...
486    ///     } else {
487    ///         // default implementation...
488    ///     }
489    /// }
490    /// # process([('a', 'b'), ('c', 'd')].into_iter().collect());
491    /// # process(
492    /// #     [
493    /// #         ("foo".to_owned(), "abc".to_owned()),
494    /// #         ("bar".to_owned(), "def".to_owned()),
495    /// #     ]
496    /// #     .into_iter()
497    /// #     .collect(),
498    /// # );
499    /// # process([(123, 234), (345, 456)].into_iter().collect());
500    /// ```
501    ///
502    /// Custom data encoders and decoders with customizable per-type encoding
503    /// and decoding errors and optimized byte array encoding and decoding.
504    /// Full example code is available at
505    /// [`examples/encode.rs`](https://github.com/zheland/try-specialize/blob/v0.1.2/examples/encode.rs).
506    /// ```rust
507    /// # use core::convert::Infallible;
508    /// # use core::{array, slice};
509    /// # use std::io::{self, Read, Write};
510    /// #
511    /// # use try_specialize::{Specialization, TypeFn};
512    /// #
513    /// # pub trait Encode {
514    /// #     type EncodeError;
515    /// #     fn encode_to<W>(&self, writer: &mut W) -> Result<(), Self::EncodeError>
516    /// #     where
517    /// #         W: ?Sized + Write;
518    /// # }
519    /// #
520    /// # pub trait Decode: Sized {
521    /// #     type DecodeError;
522    /// #     fn decode_from<R>(reader: &mut R) -> Result<Self, Self::DecodeError>
523    /// #     where
524    /// #         R: ?Sized + Read;
525    /// # }
526    /// #
527    /// # impl Encode for () {
528    /// #     type EncodeError = Infallible;
529    /// #
530    /// #     #[inline]
531    /// #     fn encode_to<W>(&self, _writer: &mut W) -> Result<(), Self::EncodeError>
532    /// #     where
533    /// #         W: ?Sized + Write,
534    /// #     {
535    /// #         Ok(())
536    /// #     }
537    /// # }
538    /// #
539    /// # impl Decode for () {
540    /// #     type DecodeError = Infallible;
541    /// #
542    /// #     #[inline]
543    /// #     fn decode_from<R>(_reader: &mut R) -> Result<Self, Self::DecodeError>
544    /// #     where
545    /// #         R: ?Sized + Read,
546    /// #     {
547    /// #         Ok(())
548    /// #     }
549    /// # }
550    /// #
551    /// # impl<T> Encode for Box<T>
552    /// # where
553    /// #     T: Encode,
554    /// # {
555    /// #     type EncodeError = T::EncodeError;
556    /// #
557    /// #     #[inline]
558    /// #     fn encode_to<W>(&self, writer: &mut W) -> Result<(), Self::EncodeError>
559    /// #     where
560    /// #         W: ?Sized + Write,
561    /// #     {
562    /// #         T::encode_to(self, writer)
563    /// #     }
564    /// # }
565    /// #
566    /// # impl<T> Decode for Box<T>
567    /// # where
568    /// #     T: Decode,
569    /// # {
570    /// #     type DecodeError = T::DecodeError;
571    /// #
572    /// #     #[inline]
573    /// #     fn decode_from<R>(reader: &mut R) -> Result<Self, Self::DecodeError>
574    /// #     where
575    /// #         R: ?Sized + Read,
576    /// #     {
577    /// #         Ok(Self::new(T::decode_from(reader)?))
578    /// #     }
579    /// # }
580    /// #
581    /// # impl Encode for u8 {
582    /// #     type EncodeError = io::Error;
583    /// #
584    /// #     #[inline]
585    /// #     fn encode_to<W>(&self, writer: &mut W) -> Result<(), Self::EncodeError>
586    /// #     where
587    /// #         W: ?Sized + Write,
588    /// #     {
589    /// #         writer.write_all(&[*self])?;
590    /// #         Ok(())
591    /// #     }
592    /// # }
593    /// #
594    /// # impl Decode for u8 {
595    /// #     type DecodeError = io::Error;
596    /// #
597    /// #     #[inline]
598    /// #     fn decode_from<R>(reader: &mut R) -> Result<Self, Self::DecodeError>
599    /// #     where
600    /// #         R: ?Sized + Read,
601    /// #     {
602    /// #         let mut byte: Self = 0;
603    /// #         reader.read_exact(slice::from_mut(&mut byte))?;
604    /// #         Ok(byte)
605    /// #     }
606    /// # }
607    /// // ...
608    ///
609    /// impl<T> Encode for [T]
610    /// where
611    ///     T: Encode,
612    /// {
613    ///     type EncodeError = T::EncodeError;
614    ///
615    ///     #[inline]
616    ///     fn encode_to<W>(&self, writer: &mut W) -> Result<(), Self::EncodeError>
617    ///     where
618    ///         W: ?Sized + Write,
619    ///     {
620    ///         if let Some(spec) = Specialization::<[T], [u8]>::try_new() {
621    ///             // Specialize self from `[T; N]` to `[u32; N]`
622    ///             let bytes: &[u8] = spec.specialize_ref(self);
623    ///             // Map type specialization to its associated error specialization.
624    ///             let spec_err = spec.rev().map::<MapToEncodeError>();
625    ///             writer
626    ///                 .write_all(bytes)
627    ///                 // Specialize error from `io::Error` to `Self::EncodeError`.
628    ///                 .map_err(|err| spec_err.specialize(err))?;
629    ///         } else {
630    ///             for item in self {
631    ///                 item.encode_to(writer)?;
632    ///             }
633    ///         }
634    ///         Ok(())
635    ///     }
636    /// }
637    ///
638    /// // ...
639    /// # impl<T, const N: usize> Encode for [T; N]
640    /// # where
641    /// #     T: Encode,
642    /// # {
643    /// #     type EncodeError = T::EncodeError;
644    /// #
645    /// #     #[inline]
646    /// #     fn encode_to<W>(&self, writer: &mut W) -> Result<(), Self::EncodeError>
647    /// #     where
648    /// #         W: ?Sized + Write,
649    /// #     {
650    /// #         self.as_slice().encode_to(writer)
651    /// #     }
652    /// # }
653    /// #
654    /// # impl<T, const N: usize> Decode for [T; N]
655    /// # where
656    /// #     T: Decode + Default,
657    /// # {
658    /// #     type DecodeError = T::DecodeError;
659    /// #
660    /// #     #[inline]
661    /// #     fn decode_from<R>(reader: &mut R) -> Result<Self, Self::DecodeError>
662    /// #     where
663    /// #         R: ?Sized + Read,
664    /// #     {
665    /// #         let spec = Specialization::<[T; N], [u8; N]>::try_new();
666    /// #
667    /// #         if let Some(spec) = spec {
668    /// #             let mut array = [0; N];
669    /// #             reader
670    /// #                 .read_exact(&mut array)
671    /// #                 // Specialize `<[u8; N]>::Error` to `<[T; N]>::Error`
672    /// #                 .map_err(|err| spec.rev().map::<MapToDecodeError>().specialize(err))?;
673    /// #             // Specialize `[u8; N]` to `[T; N]`
674    /// #             let array = spec.rev().specialize(array);
675    /// #             Ok(array)
676    /// #         } else {
677    /// #             // In real code it can be done without `Default` bound.
678    /// #             // But then the code would be unnecessarily complex for the example.
679    /// #             let mut array = array::from_fn(|_| T::default());
680    /// #             for item in &mut array {
681    /// #                 *item = T::decode_from(reader)?;
682    /// #             }
683    /// #             Ok(array)
684    /// #         }
685    /// #     }
686    /// # }
687    /// #
688    /// # struct MapToEncodeError;
689    /// #
690    /// # impl<T> TypeFn<T> for MapToEncodeError
691    /// # where
692    /// #     T: ?Sized + Encode,
693    /// # {
694    /// #     type Output = T::EncodeError;
695    /// # }
696    /// #
697    /// # struct MapToDecodeError;
698    /// # impl<T> TypeFn<T> for MapToDecodeError
699    /// # where
700    /// #     T: Decode,
701    /// # {
702    /// #     type Output = T::DecodeError;
703    /// # }
704    /// #
705    /// # let mut array_buf = [0; 8];
706    /// # let mut buf = &mut array_buf[..];
707    /// # [1_u8, 2, 3].encode_to(&mut buf).unwrap();
708    /// # 4_u8.encode_to(&mut buf).unwrap();
709    /// # [(), (), (), ()].encode_to(&mut buf).unwrap();
710    /// # [5_u8, 6, 7, 8].map(Box::new).encode_to(&mut buf).unwrap();
711    /// # assert!(9_u8.encode_to(&mut buf).is_err());
712    /// # assert!([9_u8, 10].encode_to(&mut buf).is_err());
713    /// # ().encode_to(&mut buf).unwrap();
714    /// # [(), (), ()].encode_to(&mut buf).unwrap();
715    /// # assert!([9_u8, 10].map(Box::new).encode_to(&mut buf).is_err());
716    /// # assert_eq!(array_buf, [1, 2, 3, 4, 5, 6, 7, 8]);
717    /// #
718    /// # let buf = &mut array_buf.as_slice();
719    /// # assert_eq!(u8::decode_from(buf).unwrap(), 1);
720    /// # assert_eq!(<[u8; 4]>::decode_from(buf).unwrap(), [2, 3, 4, 5]);
721    /// # assert_eq!(<[(); 16]>::decode_from(buf).unwrap(), [(); 16]);
722    /// # assert_eq!(<[u8; 1]>::decode_from(buf).unwrap(), [6]);
723    /// # assert_eq!(
724    /// #     <[Box<u8>; 2]>::decode_from(buf).unwrap(),
725    /// #     [Box::new(7), Box::new(8)]
726    /// # );
727    /// # assert!(u8::decode_from(buf).is_err());
728    /// # assert!(<[u8; 1]>::decode_from(buf).is_err());
729    /// # assert_eq!(<[(); 2]>::decode_from(buf).unwrap(), [(); 2]);
730    /// # assert!(<[Box<u8>; 2]>::decode_from(buf).is_err());
731    /// ```
732    #[inline]
733    #[must_use]
734    pub const fn map<M>(
735        &self,
736    ) -> Specialization<<M as TypeFn<T1>>::Output, <M as TypeFn<T2>>::Output>
737    where
738        M: TypeFn<T1> + TypeFn<T2>,
739    {
740        Specialization(PhantomData, PhantomData)
741    }
742
743    /// Infallibly specialize value with type `T1` as `T2`.
744    ///
745    /// This method can only be called for a `Specialization<T1, T2>` whose
746    /// existing instance indicates that types `T1` and `T2` are equivalent.
747    ///
748    /// For simple cases consider using [`TrySpecialize`] methods like
749    /// [`try_specialize`],
750    /// [`try_specialize_from`], and
751    /// [`try_specialize_static`] instead.
752    ///
753    /// [`TrySpecialize`]: crate::TrySpecialize
754    /// [`try_specialize`]: crate::TrySpecialize::try_specialize
755    /// [`try_specialize_from`]: crate::TrySpecialize::try_specialize_from
756    /// [`try_specialize_static`]: crate::TrySpecialize::try_specialize_static
757    #[inline]
758    pub fn specialize(&self, value: T1) -> T2
759    where
760        T1: Sized,
761        T2: Sized,
762    {
763        // SAFETY: `Specialization` can only be constructed if `T1` and
764        // `T2` types are equal.
765        // SAFETY: `T` and `ManuallyDrop<T>` have the same layout.
766        unsafe { transmute_copy::<T1, T2>(&ManuallyDrop::new(value)) }
767    }
768
769    /// Infallibly specialize value with type `&T1` as `&T2`.
770    ///
771    /// This method can only be called for a `Specialization<T1, T2>` whose
772    /// existing instance indicates that types `T1` and `T2` are equivalent.
773    ///
774    /// For simple cases consider using [`TrySpecialize`] methods like
775    /// [`try_specialize_ref`],
776    /// [`try_specialize_from_ref`], and
777    /// [`try_specialize_ref_static`] instead.
778    ///
779    /// [`TrySpecialize`]: crate::TrySpecialize
780    /// [`try_specialize_ref`]: crate::TrySpecialize::try_specialize_ref
781    /// [`try_specialize_from_ref`]: crate::TrySpecialize::try_specialize_from_ref
782    /// [`try_specialize_ref_static`]: crate::TrySpecialize::try_specialize_ref_static
783    #[inline]
784    pub const fn specialize_ref<'a>(&self, value: &'a T1) -> &'a T2 {
785        // SAFETY: `Specialization` can only be constructed if `T1` and
786        // `T2` types are equal.
787        unsafe { transmute_copy::<&'a T1, &'a T2>(&value) }
788    }
789
790    /// Infallibly specialize value with type `&mut T1` as `&mut T2`.
791    ///
792    /// This method can only be called for a `Specialization<T1, T2>` whose
793    /// existing instance indicates that types `T1` and `T2` are equivalent.
794    ///
795    /// For simple cases consider using [`TrySpecialize`] methods like
796    /// [`try_specialize_mut`],
797    /// [`try_specialize_from_mut`], and
798    /// [`try_specialize_mut_static`] instead.
799    ///
800    /// [`TrySpecialize`]: crate::TrySpecialize
801    /// [`try_specialize_mut`]: crate::TrySpecialize::try_specialize_mut
802    /// [`try_specialize_from_mut`]: crate::TrySpecialize::try_specialize_from_mut
803    /// [`try_specialize_mut_static`]: crate::TrySpecialize::try_specialize_mut_static
804    #[inline]
805    pub fn specialize_mut<'a>(&self, value: &'a mut T1) -> &'a mut T2 {
806        // SAFETY: `Specialization` can only be constructed if `T1` and
807        // `T2` types are equal.
808        unsafe { transmute_copy::<&'a mut T1, &'a mut T2>(&value) }
809    }
810}
811
812impl<T1, T2> Copy for Specialization<T1, T2>
813where
814    T1: ?Sized,
815    T2: ?Sized,
816{
817}
818
819impl<T1, T2> Clone for Specialization<T1, T2>
820where
821    T1: ?Sized,
822    T2: ?Sized,
823{
824    #[inline]
825    fn clone(&self) -> Self {
826        *self
827    }
828}
829
830impl<T1, T2> Eq for Specialization<T1, T2>
831where
832    T1: ?Sized,
833    T2: ?Sized,
834{
835}
836
837impl<T1, T2> PartialEq for Specialization<T1, T2>
838where
839    T1: ?Sized,
840    T2: ?Sized,
841{
842    #[inline]
843    fn eq(&self, _: &Self) -> bool {
844        true
845    }
846}
847
848impl<T1, T2> Ord for Specialization<T1, T2>
849where
850    T1: ?Sized,
851    T2: ?Sized,
852{
853    #[inline]
854    fn cmp(&self, _: &Self) -> Ordering {
855        Ordering::Equal
856    }
857}
858
859impl<T1, T2> PartialOrd for Specialization<T1, T2>
860where
861    T1: ?Sized,
862    T2: ?Sized,
863{
864    #[inline]
865    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
866        Some(self.cmp(other))
867    }
868}
869
870impl<T1, T2> Hash for Specialization<T1, T2>
871where
872    T1: ?Sized,
873    T2: ?Sized,
874{
875    #[inline]
876    fn hash<H: Hasher>(&self, _: &mut H) {}
877}
878
879impl<T1, T2> Debug for Specialization<T1, T2>
880where
881    T1: ?Sized,
882    T2: ?Sized,
883{
884    #[inline]
885    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
886        match self {
887            Self(f0, f1) => f
888                .debug_tuple("Specialization")
889                .field(&f0)
890                .field(&f1)
891                .finish(),
892        }
893    }
894}
895
896#[cfg(test)]
897mod tests {
898    #[cfg(feature = "std")]
899    use alloc::format;
900    #[cfg(feature = "std")]
901    use core::hash::{Hash, Hasher};
902    #[cfg(feature = "std")]
903    use std::hash::DefaultHasher;
904
905    use crate::{LifetimeFree, Specialization, TypeFn};
906
907    #[expect(clippy::arithmetic_side_effects, reason = "okay in tests")]
908    fn specialized_inc_if_i32_dec_if_u32<T>(value: T) -> T
909    where
910        T: LifetimeFree,
911    {
912        if let Some(spec) = Specialization::<T, i32>::try_new() {
913            spec.rev().specialize(spec.specialize(value) + 1)
914        } else if let Some(spec) = Specialization::<T, u32>::try_new() {
915            spec.rev().specialize(spec.specialize(value) - 1)
916        } else {
917            value
918        }
919    }
920
921    #[test]
922    fn test_checked_specialization() {
923        assert_eq!(specialized_inc_if_i32_dec_if_u32(123_i32), 124);
924        assert_eq!(specialized_inc_if_i32_dec_if_u32(123_u32), 122);
925        assert_eq!(specialized_inc_if_i32_dec_if_u32(123_i16), 123);
926        assert_eq!(specialized_inc_if_i32_dec_if_u32(123_u16), 123);
927    }
928
929    #[test]
930    fn test_checked_specialization_map() {
931        #[derive(Eq, PartialEq, Debug)]
932        struct Wrapper<T>(T);
933
934        fn as_wrapped_u32_ref<T>(value: &Wrapper<T>) -> Option<&Wrapper<u32>>
935        where
936            T: LifetimeFree,
937        {
938            struct MapIntoWrapper;
939            impl<T> TypeFn<T> for MapIntoWrapper {
940                type Output = Wrapper<T>;
941            }
942
943            Some(
944                Specialization::<T, u32>::try_new()?
945                    .map::<MapIntoWrapper>()
946                    .specialize_ref(value),
947            )
948        }
949
950        assert_eq!(
951            as_wrapped_u32_ref(&Wrapper(123_u32)),
952            Some(&Wrapper(123_u32))
953        );
954        assert_eq!(as_wrapped_u32_ref(&Wrapper(123_i32)), None);
955        assert_eq!(as_wrapped_u32_ref(&Wrapper((1, 2, 3, 4))), None);
956    }
957
958    #[cfg(feature = "std")]
959    #[test]
960    fn test_checked_specialization_basic_trait_impls() {
961        #[expect(clippy::unwrap_used, reason = "Okay in tests")]
962        let spec1 = Specialization::<u32, u32>::try_new().unwrap();
963        let spec2 = spec1;
964        #[expect(clippy::clone_on_copy, reason = "Okay in tests")]
965        let _ = spec1.clone();
966        assert_eq!(spec1, spec2);
967        assert!(spec1 <= spec2);
968
969        let mut hasher = DefaultHasher::new();
970        let hash1 = hasher.finish();
971        spec1.hash(&mut hasher);
972        let hash2 = hasher.finish();
973        assert_eq!(hash1, hash2);
974
975        assert_eq!(
976            format!("{spec1:?}"),
977            "Specialization(PhantomData<u32>, PhantomData<u32>)"
978        );
979    }
980}