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}