try_specialize/lib.rs
1#![no_std]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3
4//! The `try-specialize` crate provides limited, [zero-cost](#zero-cost)
5//! specialization in generic context on stable Rust.
6//!
7//! ```rust
8//! use try_specialize::TrySpecialize;
9//!
10//! fn example_specialize_by_value<T>(value: T) -> Result<u32, T> {
11//! value.try_specialize()
12//! // Same as: `<T as TrySpecialize>::try_specialize::<u32>(value)`.
13//! // `try_specialize::<T>` specializes from `Self` to `T, where T: LifetimeFree`.
14//! }
15//!
16//! fn example_specialize_by_ref<T: ?Sized>(value: &T) -> Option<&str> {
17//! value.try_specialize_ref()
18//! // Same as: `<T as TrySpecialize>::try_specialize_ref::<str>(value)`.
19//! // `try_specialize_ref::<T>` specializes from `&Self` to `&T, where T: LifetimeFree`.
20//! }
21//!
22//! assert_eq!(example_specialize_by_value(123_u32), Ok(123));
23//! assert_eq!(example_specialize_by_value(123_i32), Err(123));
24//! assert_eq!(example_specialize_by_ref("foo"), Some("foo"));
25//! assert_eq!(example_specialize_by_ref(&123_u32), None);
26//! assert_eq!(example_specialize_by_ref(&[1, 2, 3]), None);
27//! ```
28//!
29//! # Introduction
30//!
31//! While specialization in Rust can be a tempting solution in many use cases,
32//! it is usually more idiomatic to use traits instead. Traits are the idiomatic
33//! way to achieve polymorphism in Rust, promoting better code clarity,
34//! reusability, and maintainability.
35//!
36//! However, specialization can be suitable when you need to optimize
37//! performance by providing specialized implementations for some types without
38//! altering the code logic. It's also useful in specific, type-level
39//! programming use cases like comparisons between types from different
40//! libraries.
41//!
42//! For a simple use cases, consider the [`castaway`] crate, which offers a much
43//! simpler API. On nightly Rust, consider using [`min_specialization`] feature
44//! instead. The Rust standard library already uses [`min_specialization`] for
45//! many optimizations. For a more detailed comparison, see the
46//! [Alternative crates](#alternative-crates) section below.
47//!
48//! # About
49//!
50//! This crate offers a comprehensive API for addressing various specialization
51//! challenges, reducing the need for unsafe code. It provides specialization
52//! from unconstrained types, to unconstrained types, between 'static types,
53//! and between type references and mutable references, and more.
54//!
55//! <a name="zero-cost"></a> Library tests ensure that specializations are
56//! performed at compile time and are fully optimized with no runtime cost at
57//! `opt-level >= 1`. Note that the [release] profile uses `opt-level = 3`
58//! by default.
59//!
60//! # Usage
61//!
62//! Add this to your `Cargo.toml`:
63//!
64//! ```toml
65//! [dependencies]
66//! try-specialize = "0.1.2"
67//! ```
68//!
69//! Then, you can use [`TrySpecialize`] trait methods like
70//! [`TrySpecialize::try_specialize`], [`TrySpecialize::try_specialize_ref`] and
71//! [`TrySpecialize::try_specialize_static`]. To check the possibility of
72//! specialization in advance and use it infallibly multiple times, including
73//! reversed or mapped specialization, use [`Specialization`] struct methods.
74//!
75//! Note that unlike casting, [subtyping], and [coercion], specialization does
76//! not alter the underlying type or data. It merely qualifies the underlying
77//! types of generics, succeeding only when the underlying types of `T1` and
78//! `T2` are equal.
79//!
80//! # Examples
81//!
82//! Specialize type to any [`LifetimeFree`] type:
83//! ```rust
84//! # #[cfg(feature = "alloc")] {
85//! use try_specialize::TrySpecialize;
86//!
87//! fn func<T>(value: T) {
88//! match value.try_specialize::<(u32, String)>() {
89//! Ok(value) => specialized_impl(value),
90//! Err(value) => default_impl(value),
91//! }
92//! }
93//!
94//! fn specialized_impl(_value: (u32, String)) {}
95//! fn default_impl<T>(_value: T) {}
96//! # func((42_u32, "abc".to_owned()));
97//! # func((42_i32, "abc".to_owned()));
98//! # }
99//! ```
100//!
101//! Specialize `'static` type to any `'static` type:
102//! ```rust
103//! use try_specialize::TrySpecialize;
104//!
105//! fn func<T>(value: T)
106//! where
107//! T: 'static,
108//! {
109//! match value.try_specialize_static::<(u32, &'static str)>() {
110//! Ok(value) => specialized_impl(value),
111//! Err(value) => default_impl(value),
112//! }
113//! }
114//!
115//! fn specialized_impl(_value: (u32, &'static str)) {}
116//! fn default_impl<T>(_value: T) {}
117//! # func((42_u32, "abc"));
118//! # func((42_i32, "abc"));
119//! ```
120//!
121//! Specialize `Sized` or `Unsized` type reference to any [`LifetimeFree`] type
122//! reference:
123//! ```rust
124//! use try_specialize::TrySpecialize;
125//!
126//! fn func<T>(value: &T)
127//! where
128//! T: ?Sized, // Relax the implicit `Sized` bound.
129//! {
130//! match value.try_specialize_ref::<str>() {
131//! Some(value) => specialized_impl(value),
132//! None => default_impl(value),
133//! }
134//! }
135//!
136//! fn specialized_impl(_value: &str) {}
137//! fn default_impl<T: ?Sized>(_value: &T) {}
138//! # func("abc");
139//! # func(&42);
140//! ```
141//!
142//! Specialize `Sized` or `Unsized` type mutable reference to any
143//! [`LifetimeFree`] type mutable reference:
144//! ```rust
145//! use try_specialize::TrySpecialize;
146//!
147//! fn func<T>(value: &mut T)
148//! where
149//! T: ?Sized, // Relax the implicit `Sized` bound.
150//! {
151//! match value.try_specialize_mut::<[u8]>() {
152//! Some(value) => specialized_impl(value),
153//! None => default_impl(value),
154//! }
155//! }
156//!
157//! fn specialized_impl(_value: &mut [u8]) {}
158//! fn default_impl<T: ?Sized>(_value: &mut T) {}
159//! # func(&mut [1_u8, 2, 3][..]);
160//! # func(&mut [1_i8, 2, 3][..]);
161//! ```
162//!
163//! Specialize a third-party library container with generic types:
164//! ```rust
165//! use try_specialize::{Specialization, TypeFn};
166//!
167//! fn func<K, V>(value: hashbrown::HashMap<K, V>) {
168//! struct MapIntoHashMap;
169//! impl<K, V> TypeFn<(K, V)> for MapIntoHashMap {
170//! type Output = hashbrown::HashMap<K, V>;
171//! }
172//!
173//! if let Some(spec) = Specialization::<(K, V), (u32, char)>::try_new() {
174//! let spec = spec.map::<MapIntoHashMap>();
175//! let value: hashbrown::HashMap<u32, char> = spec.specialize(value);
176//! specialized_impl(value);
177//! } else {
178//! default_impl(value);
179//! }
180//! }
181//!
182//! fn default_impl<K, V>(_value: hashbrown::HashMap<K, V>) {}
183//! fn specialized_impl(_value: hashbrown::HashMap<u32, char>) {}
184//! # func([(12_u32, 'a'), (23_u32, 'b')].into_iter().collect());
185//! # func([(12_i32, 'a'), (23_i32, 'b')].into_iter().collect());
186//! ```
187//!
188//! For a more comprehensive example, see the [`examples/encode.rs`], which
189//! implements custom data encoders and decoders with per-type encoding and
190//! decoding errors and optimized byte array encoding and decoding.
191//! The part of this example related to the `Encode` implementation for a slice:
192//! ```rust
193//! # use core::convert::Infallible;
194//! # use core::{array, slice};
195//! # use std::io::{self, Read, Write};
196//! #
197//! # use try_specialize::{Specialization, TypeFn};
198//! #
199//! # pub trait Encode {
200//! # type EncodeError;
201//! # fn encode_to<W>(&self, writer: &mut W) -> Result<(), Self::EncodeError>
202//! # where
203//! # W: ?Sized + Write;
204//! # }
205//! #
206//! # pub trait Decode: Sized {
207//! # type DecodeError;
208//! # fn decode_from<R>(reader: &mut R) -> Result<Self, Self::DecodeError>
209//! # where
210//! # R: ?Sized + Read;
211//! # }
212//! #
213//! # impl Encode for () {
214//! # type EncodeError = Infallible;
215//! #
216//! # #[inline]
217//! # fn encode_to<W>(&self, _writer: &mut W) -> Result<(), Self::EncodeError>
218//! # where
219//! # W: ?Sized + Write,
220//! # {
221//! # Ok(())
222//! # }
223//! # }
224//! #
225//! # impl Decode for () {
226//! # type DecodeError = Infallible;
227//! #
228//! # #[inline]
229//! # fn decode_from<R>(_reader: &mut R) -> Result<Self, Self::DecodeError>
230//! # where
231//! # R: ?Sized + Read,
232//! # {
233//! # Ok(())
234//! # }
235//! # }
236//! #
237//! # impl<T> Encode for Box<T>
238//! # where
239//! # T: Encode,
240//! # {
241//! # type EncodeError = T::EncodeError;
242//! #
243//! # #[inline]
244//! # fn encode_to<W>(&self, writer: &mut W) -> Result<(), Self::EncodeError>
245//! # where
246//! # W: ?Sized + Write,
247//! # {
248//! # T::encode_to(self, writer)
249//! # }
250//! # }
251//! #
252//! # impl<T> Decode for Box<T>
253//! # where
254//! # T: Decode,
255//! # {
256//! # type DecodeError = T::DecodeError;
257//! #
258//! # #[inline]
259//! # fn decode_from<R>(reader: &mut R) -> Result<Self, Self::DecodeError>
260//! # where
261//! # R: ?Sized + Read,
262//! # {
263//! # Ok(Self::new(T::decode_from(reader)?))
264//! # }
265//! # }
266//! #
267//! # impl Encode for u8 {
268//! # type EncodeError = io::Error;
269//! #
270//! # #[inline]
271//! # fn encode_to<W>(&self, writer: &mut W) -> Result<(), Self::EncodeError>
272//! # where
273//! # W: ?Sized + Write,
274//! # {
275//! # writer.write_all(&[*self])?;
276//! # Ok(())
277//! # }
278//! # }
279//! #
280//! # impl Decode for u8 {
281//! # type DecodeError = io::Error;
282//! #
283//! # #[inline]
284//! # fn decode_from<R>(reader: &mut R) -> Result<Self, Self::DecodeError>
285//! # where
286//! # R: ?Sized + Read,
287//! # {
288//! # let mut byte: Self = 0;
289//! # reader.read_exact(slice::from_mut(&mut byte))?;
290//! # Ok(byte)
291//! # }
292//! # }
293//! // ...
294//!
295//! impl<T> Encode for [T]
296//! where
297//! T: Encode,
298//! {
299//! type EncodeError = T::EncodeError;
300//!
301//! #[inline]
302//! fn encode_to<W>(&self, writer: &mut W) -> Result<(), Self::EncodeError>
303//! where
304//! W: ?Sized + Write,
305//! {
306//! if let Some(spec) = Specialization::<[T], [u8]>::try_new() {
307//! // Specialize self from `[T; N]` to `[u32; N]`
308//! let bytes: &[u8] = spec.specialize_ref(self);
309//! // Map type specialization to its associated error specialization.
310//! let spec_err = spec.rev().map::<MapToEncodeError>();
311//! writer
312//! .write_all(bytes)
313//! // Specialize error from `io::Error` to `Self::EncodeError`.
314//! .map_err(|err| spec_err.specialize(err))?;
315//! } else {
316//! for item in self {
317//! item.encode_to(writer)?;
318//! }
319//! }
320//! Ok(())
321//! }
322//! }
323//!
324//! // ...
325//! # impl<T, const N: usize> Encode for [T; N]
326//! # where
327//! # T: Encode,
328//! # {
329//! # type EncodeError = T::EncodeError;
330//! #
331//! # #[inline]
332//! # fn encode_to<W>(&self, writer: &mut W) -> Result<(), Self::EncodeError>
333//! # where
334//! # W: ?Sized + Write,
335//! # {
336//! # self.as_slice().encode_to(writer)
337//! # }
338//! # }
339//! #
340//! # impl<T, const N: usize> Decode for [T; N]
341//! # where
342//! # T: Decode + Default,
343//! # {
344//! # type DecodeError = T::DecodeError;
345//! #
346//! # #[inline]
347//! # fn decode_from<R>(reader: &mut R) -> Result<Self, Self::DecodeError>
348//! # where
349//! # R: ?Sized + Read,
350//! # {
351//! # let spec = Specialization::<[T; N], [u8; N]>::try_new();
352//! #
353//! # if let Some(spec) = spec {
354//! # let mut array = [0; N];
355//! # reader
356//! # .read_exact(&mut array)
357//! # // Specialize `<[u8; N]>::Error` to `<[T; N]>::Error`
358//! # .map_err(|err| spec.rev().map::<MapToDecodeError>().specialize(err))?;
359//! # // Specialize `[u8; N]` to `[T; N]`
360//! # let array = spec.rev().specialize(array);
361//! # Ok(array)
362//! # } else {
363//! # // In real code it can be done without `Default` bound.
364//! # // But then the code would be unnecessarily complex for the example.
365//! # let mut array = array::from_fn(|_| T::default());
366//! # for item in &mut array {
367//! # *item = T::decode_from(reader)?;
368//! # }
369//! # Ok(array)
370//! # }
371//! # }
372//! # }
373//! #
374//! # struct MapToEncodeError;
375//! #
376//! # impl<T> TypeFn<T> for MapToEncodeError
377//! # where
378//! # T: ?Sized + Encode,
379//! # {
380//! # type Output = T::EncodeError;
381//! # }
382//! #
383//! # struct MapToDecodeError;
384//! # impl<T> TypeFn<T> for MapToDecodeError
385//! # where
386//! # T: Decode,
387//! # {
388//! # type Output = T::DecodeError;
389//! # }
390//! #
391//! # let mut array_buf = [0; 8];
392//! # let mut buf = &mut array_buf[..];
393//! # [1_u8, 2, 3].encode_to(&mut buf).unwrap();
394//! # 4_u8.encode_to(&mut buf).unwrap();
395//! # [(), (), (), ()].encode_to(&mut buf).unwrap();
396//! # [5_u8, 6, 7, 8].map(Box::new).encode_to(&mut buf).unwrap();
397//! # assert!(9_u8.encode_to(&mut buf).is_err());
398//! # assert!([9_u8, 10].encode_to(&mut buf).is_err());
399//! # ().encode_to(&mut buf).unwrap();
400//! # [(), (), ()].encode_to(&mut buf).unwrap();
401//! # assert!([9_u8, 10].map(Box::new).encode_to(&mut buf).is_err());
402//! # assert_eq!(array_buf, [1, 2, 3, 4, 5, 6, 7, 8]);
403//! #
404//! # let buf = &mut array_buf.as_slice();
405//! # assert_eq!(u8::decode_from(buf).unwrap(), 1);
406//! # assert_eq!(<[u8; 4]>::decode_from(buf).unwrap(), [2, 3, 4, 5]);
407//! # assert_eq!(<[(); 16]>::decode_from(buf).unwrap(), [(); 16]);
408//! # assert_eq!(<[u8; 1]>::decode_from(buf).unwrap(), [6]);
409//! # assert_eq!(
410//! # <[Box<u8>; 2]>::decode_from(buf).unwrap(),
411//! # [Box::new(7), Box::new(8)]
412//! # );
413//! # assert!(u8::decode_from(buf).is_err());
414//! # assert!(<[u8; 1]>::decode_from(buf).is_err());
415//! # assert_eq!(<[(); 2]>::decode_from(buf).unwrap(), [(); 2]);
416//! # assert!(<[Box<u8>; 2]>::decode_from(buf).is_err());
417//! ```
418//!
419//! Find values by type in generic composite types:
420//! ```rust
421//! use try_specialize::{LifetimeFree, TrySpecialize};
422//!
423//! pub trait ConsListLookup {
424//! fn find<T>(&self) -> Option<&T>
425//! where
426//! T: ?Sized + LifetimeFree;
427//! }
428//!
429//! impl ConsListLookup for () {
430//! #[inline]
431//! fn find<T>(&self) -> Option<&T>
432//! where
433//! T: ?Sized + LifetimeFree,
434//! {
435//! None
436//! }
437//! }
438//!
439//! impl<T1, T2> ConsListLookup for (T1, T2)
440//! where
441//! T2: ConsListLookup,
442//! {
443//! #[inline]
444//! fn find<T>(&self) -> Option<&T>
445//! where
446//! T: ?Sized + LifetimeFree,
447//! {
448//! self.0.try_specialize_ref().or_else(|| self.1.find::<T>())
449//! }
450//! }
451//!
452//! #[derive(Eq, PartialEq, Debug)]
453//! struct StaticStr(&'static str);
454//! // SAFETY: It is safe to implement `LifetimeFree` for structs with no
455//! // parameters.
456//! unsafe impl LifetimeFree for StaticStr {}
457//!
458//! let input = (
459//! 123_i32,
460//! (
461//! [1_u32, 2, 3, 4],
462//! (1_i32, (StaticStr("foo"), (('a', false), ()))),
463//! ),
464//! );
465//!
466//! assert_eq!(input.find::<u32>(), None);
467//! assert_eq!(input.find::<i32>(), Some(&123_i32));
468//! assert_eq!(input.find::<[u32; 4]>(), Some(&[1, 2, 3, 4]));
469//! assert_eq!(input.find::<[u32]>(), None);
470//! assert_eq!(input.find::<StaticStr>(), Some(&StaticStr("foo")));
471//! assert_eq!(input.find::<char>(), None);
472//! assert_eq!(input.find::<(char, bool)>(), Some(&('a', false)));
473//! ```
474//!
475//! # Documentation
476//!
477//! [API Documentation]
478//!
479//! # Feature flags
480//!
481//! - `alloc` (implied by `std`, enabled by default): enables [`LifetimeFree`]
482//! implementations for `alloc` types, like `Box`, `Arc`, `String`, `Vec`,
483//! `BTreeMap` etc.
484//! - `std` (enabled by default): enables `alloc` feature and [`LifetimeFree`]
485//! implementations for `std` types, like `OsStr`, `Path`, `PathBuf`,
486//! `Instant`, `HashMap` etc.
487//! - `unreliable`: enables functions, methods and macros that rely on Rust
488//! standard library undocumented behavior. Refer to the [`unreliable`] module
489//! documentation for details.
490//!
491//! # How it works
492//!
493//! - Type comparison between `'static` types compares their [`TypeId::of`]s.
494//! - Type comparison between unconstrained and [`LifetimeFree`] type treats
495//! them as `'static` and compares their [`TypeId::of`]s.
496//! - Specialization relies on type comparison and [`transmute_copy`] when the
497//! equality of types is established.
498//! - Unreliable trait implementation checks are performed using an expected,
499//! but undocumented behavior of the Rust stdlib [`PartialEq`] implementation
500//! for [`Arc<T>`]. [`Arc::eq`] uses fast path comparing references before
501//! comparing data if `T` implements [`Eq`].
502//!
503//! # Alternative crates
504//!
505//! - [`castaway`]: A similar crate with a much simpler macro-based API. The
506//! macro uses [Autoref-Based Specialization] and automatically determines the
507//! appropriate type of specialization, making it much easier to use. However,
508//! if no specialization is applicable because of the same [Autoref-Based
509//! Specialization], the compiler generates completely unclear errors, which
510//! makes it difficult to use it in complex cases. Internally uses `unsafe`
511//! code for type comparison and specialization.
512//! - [`coe-rs`]: Smaller and simpler, but supports only static types and don't
513//! safely combine type equality check and specialization. Internally uses
514//! `unsafe` code for type specialization.
515//! - [`downcast-rs`]: Specialized on trait objects (`dyn`) downcasting. Can't
516//! be used to specialize unconstrained types.
517//! - [`syllogism`] and [`syllogism_macro`]: Requires to provide all possible
518//! types to macro that generate a lot of boilerplate code and can't be used
519//! to specialize stdlib types because of orphan rules.
520//! - [`specialize`](https://crates.io/crates/specialize): Requires nightly.
521//! Adds a simple macro to inline nightly [`min_specialization`] usage into
522//! simple `if let` expressions.
523//! - [`specialized-dispatch`]: Requires nightly. Adds a macro to inline nightly
524//! [`min_specialization`] usage into a `match`-like macro.
525//! - [`spez`]: Specializes expression types, using [Autoref-Based
526//! Specialization]. It won't works in generic context but can be used in the
527//! code generated by macros.
528//! - [`impls`]: Determine if a type implements a trait. Can't detect erased
529//! type bounds, so not applicable in generic context, but can be used in the
530//! code generated by macros.
531//!
532//! ## Comparison of libraries supporting specialization in generic context:
533//!
534//! | | crate <br /> `try-specialize` | crate <br /> [`castaway`] | crate <br /> [`coe-rs`] | crate <br /> [`downcast-rs`] | crate <br /> [`syllogism`] | [`min_spec...`] <br /> nightly feature | crate <br /> [`specialize`](https://crates.io/crates/specialize) | crate <br /> [`spec...ch`]
535//! | --: | :--: | :--: | :--: | :--: | :--: | :--: | :--: | :--: |
536//! | Checked version | `0.1.2` | `0.2.3` | `0.1.2` | `1.2.1` | `0.1.3` | N/A | `0.0.3` | `0.2.1` |
537//! | Rust toolchain | **Stable** | **Stable** | **Stable** | **Stable** | **Stable** | Nightly | Nightly | Nightly |
538//! | API complexity | Complex | **Simple** | **Simple** | Moderate | **Simple** | **Simple** | **Simple** | **Simple** |
539//! | API difficulty | Difficult | **Easy** | **Easy** | Moderate | Moderate | **Easy** | **Easy** | Moderate |
540//! | Zero-cost (compile-time optimized) | **YES** | **YES** | **YES** | no | **YES** | **YES** | **YES** | **YES** |
541//! | Safely combines type eq check and specialization | **YES** | **YES** | no | **YES** | **YES** | **YES** |**YES** | **YES** |
542//! | Specialize value references | **YES** | **YES** | **YES** | N/A | **YES** | **YES** | **YES** | no |
543//! | Specialize values | **YES** | **YES** | no | N/A | **YES** | **YES** | **YES** | **YES** |
544//! | Specialize values without consume on failure | **YES** | **YES** | no | N/A | **YES** | **YES** | no | **YES** |
545//! | Limited non-static value specialization | **YES** | **YES** | no | N/A | **YES** | **YES** | **YES** | **YES** |
546//! | Full non-static value specialization | no | no | no | N/A | **YES** | no | no | no |
547//! | Specialize trait objects (`dyn`) | N/A | N/A | N/A | **YES** | N/A | N/A | N/A | N/A |
548//! | Compare types without instantiation | **YES** | no | **YES** | no | **YES** | **YES** | **YES** | no |
549//! | Support std types | **YES** | **YES** | **YES** | **YES** | no | **YES** | **YES** | **YES** |
550//! | Specialize from unconstrained type | **YES** | **YES** | no | no | no | **YES** | **YES** | **YES** |
551//! | Specialize to unconstrained type | **YES** | no | no | no | no | **YES** | **YES** | **YES** |
552//! | Check generic implements "erased" trait | **YES**, but [`unreliable`] | no | no | no | no | **YES** | **YES** | **YES** |
553//! | Specialize to generic with added bounds | no | no | no | no | no | **YES** | **YES** | **YES** |
554//! | API based on | Traits | Macros | Traits | Macros + Traits | Traits | Language | Macros | Macros |
555//! | Type comparison implementation based on | [`TypeId`] <br /> + [`transmute`] | [`TypeId`] <br /> + [`transmute`] |[`TypeId`] | N/A | Traits | Language | Nightly <br /> feature | Nightly <br /> feature |
556//! | Type casting implementation based on | [`transmute_copy`] | [`ptr::read`] | [`transmute`] | [`std::any::Any`] | Traits | Language | Nightly <br /> feature | Nightly <br /> feature |
557//! | Implementation free of `unsafe` | no | no | no | **YES** | **YES** | **YES** | **YES** | **YES** |
558//!
559//! ## Primitive example of the value specialization using different libraries
560//!
561//! <table><tbody><tr><td style="vertical-align: top">
562//!
563//! crate `try_specialize`:
564//!
565//! ```rust
566//! use try_specialize::TrySpecialize;
567//!
568//! fn spec<T>(value: T) -> Result<u32, T> {
569//! value.try_specialize()
570//! }
571//!
572//! assert_eq!(spec(42_u32), Ok(42));
573//! assert_eq!(spec(42_i32), Err(42));
574//! assert_eq!(spec("abc"), Err("abc"));
575//! ```
576//!
577//! </td><td style="vertical-align: top">
578//!
579//! crate [`castaway`]:
580//!
581//! ```rust
582//! use castaway::cast;
583//!
584//! fn spec<T>(value: T) -> Result<u32, T> {
585//! cast!(value, _)
586//! }
587//!
588//! assert_eq!(spec(42_u32), Ok(42));
589//! assert_eq!(spec(42_i32), Err(42));
590//! assert_eq!(spec("abc"), Err("abc"));
591//! ```
592//!
593//! </td></tr><tr><td style="vertical-align: top">
594//!
595//! crate [`coe-rs`]:
596//!
597//! ```rust
598//! use coe::{is_same, Coerce};
599//!
600//! // Library don't support non-reference.
601//! // specialization. Using reference.
602//! fn spec<T>(value: &T) -> Option<&u32>
603//! where
604//! // Library don't support specialization of
605//! // unconstrained non-static types.
606//! T: 'static,
607//! {
608//! is_same::<u32, T>().then(|| value.coerce())
609//! }
610//!
611//! fn main() {
612//! assert_eq!(spec(&42_u32), Some(&42));
613//! assert_eq!(spec(&42_i32), None);
614//! assert_eq!(spec(&"abc"), None);
615//! }
616//! ```
617//!
618//! </td><td style="vertical-align: top">
619//!
620//! crates [`downcast-rs`]:
621//!
622//! ```rust
623//! use downcast_rs::{impl_downcast, DowncastSync};
624//!
625//! trait Base: DowncastSync {}
626//! impl_downcast!(sync Base);
627//!
628//! // Library requires all specializable
629//! // types to be defined in advance.
630//! impl Base for u32 {}
631//! impl Base for i32 {}
632//! impl Base for &'static str {}
633//!
634//! // Library support only trait objects (`dyn`).
635//! fn spec(value: &dyn Base) -> Option<&u32> {
636//! value.downcast_ref::<u32>()
637//! }
638//!
639//! fn main() {
640//! assert_eq!(spec(&42_u32), Some(&42));
641//! assert_eq!(spec(&42_i32), None);
642//! assert_eq!(spec(&"abc"), None);
643//! }
644//! ```
645//!
646//! </td></tr><tr><td style="vertical-align: top">
647//!
648//! crate [`specialize`](https://crates.io/crates/specialize):
649//!
650//! ```rust
651//! # #![cfg_attr(feature = "__test_nightly", feature(min_specialization))]
652//! # #[cfg(feature = "__test_nightly")] {
653//! // Requires nightly.
654//! #![feature(min_specialization)]
655//!
656//! use specialize::constrain;
657//!
658//! // Library don't support non-consuming
659//! // value specialization. Using reference.
660//! fn spec<T: ?Sized>(value: &T) -> Option<&u32> {
661//! constrain!(ref value as u32)
662//! }
663//!
664//! assert_eq!(spec(&42_u32), Some(&42));
665//! assert_eq!(spec(&42_i32), None);
666//! assert_eq!(spec("abc"), None);
667//! # }
668//! ```
669//!
670//! </td><td style="vertical-align: top">
671//!
672//! crate [`specialized-dispatch`]:
673//! ```rust
674//! # #![cfg_attr(feature = "__test_nightly", feature(min_specialization))]
675//! # #[cfg(feature = "__test_nightly")] {
676//! // Requires nightly.
677//! #![feature(min_specialization)]
678//!
679//! use specialized_dispatch::specialized_dispatch;
680//!
681//! // The library don't support using generics.
682//! // from outer item. Using `Option`.
683//! fn spec<T>(value: T) -> Option<u32> {
684//! specialized_dispatch! {
685//! T -> Option<u32>,
686//! fn (value: u32) => Some(value),
687//! default fn <T>(_: T) => None,
688//! value,
689//! }
690//! }
691//!
692//! assert_eq!(spec(42_u32), Some(42));
693//! assert_eq!(spec(42_i32), None);
694//! assert_eq!(spec("abc"), None);
695//! # }
696//! ```
697//!
698//! </td></tr><tr><td style="vertical-align: top">
699//!
700//! crates [`syllogism`] and [`syllogism_macro`]:
701//!
702//! ```rust
703//! # #[rustfmt::skip]
704//! use syllogism::{Distinction, Specialize};
705//! use syllogism_macro::impl_specialization;
706//!
707//! // Library specialization can not be
708//! // implemented for std types because of
709//! // orphan rules. Using custom local types.
710//! #[derive(Eq, PartialEq, Debug)]
711//! struct U32(u32);
712//! #[derive(Eq, PartialEq, Debug)]
713//! struct I32(i32);
714//! #[derive(Eq, PartialEq, Debug)]
715//! struct Str<'a>(&'a str);
716//!
717//! // Library requires all specializable
718//! // types to be defined in one place.
719//! impl_specialization!(
720//! type U32;
721//! type I32;
722//! type Str<'a>;
723//! );
724//!
725//! fn spec<T>(value: T) -> Result<U32, T>
726//! where
727//! T: Specialize<U32>,
728//! {
729//! match value.specialize() {
730//! Distinction::Special(value) => Ok(value),
731//! Distinction::Generic(value) => Err(value),
732//! }
733//! }
734//!
735//! assert_eq!(spec(U32(42)), Ok(U32(42)));
736//! assert_eq!(spec(I32(42_i32)), Err(I32(42)));
737//! assert_eq!(spec(Str("abc")), Err(Str("abc")));
738//! ```
739//!
740//! </td><td style="vertical-align: top">
741//!
742//! [`min_specialization`] nightly feature:
743//!
744//! ```rust
745//! # #![cfg_attr(feature = "__test_nightly", feature(min_specialization))]
746//! # #[cfg(feature = "__test_nightly")] {
747//! // Requires nightly.
748//! #![feature(min_specialization)]
749//!
750//! // The artificial example looks a bit long.
751//! // More real-world use cases are usually
752//! // on the contrary more clear and understandable.
753//! pub trait Spec: Sized {
754//! fn spec(self) -> Result<u32, Self>;
755//! }
756//!
757//! impl<T> Spec for T {
758//! default fn spec(self) -> Result<u32, Self> {
759//! Err(self)
760//! }
761//! }
762//!
763//! impl Spec for u32 {
764//! fn spec(self) -> Result<u32, Self> {
765//! Ok(self)
766//! }
767//! }
768//!
769//! assert_eq!(Spec::spec(42_u32), Ok(42));
770//! assert_eq!(Spec::spec(42_i32), Err(42));
771//! assert_eq!(Spec::spec("abc"), Err("abc"));
772//! # }
773//! ```
774//!
775//! </td></tr></tbody></table>
776//!
777//! # License
778//!
779//! Licensed under either of
780//!
781//! - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or <https://www.apache.org/licenses/LICENSE-2.0>)
782//! - MIT license ([LICENSE-MIT](LICENSE-MIT) or <https://opensource.org/licenses/MIT>)
783//!
784//! at your option.
785//!
786//! # Contribution
787//!
788//! Unless you explicitly state otherwise, any contribution intentionally
789//! submitted for inclusion in the work by you, as defined in the Apache-2.0
790//! license, shall be dual licensed as above, without any
791//! additional terms or conditions.
792//!
793//! [API Documentation]: https://docs.rs/try-specialize
794//! [subtyping]: https://doc.rust-lang.org/nomicon/subtyping.html
795//! [coercion]: https://doc.rust-lang.org/nomicon/coercions.html
796//! [release]: https://doc.rust-lang.org/cargo/reference/profiles.html#release
797//! [`min_specialization`]: https://github.com/rust-lang/rust/pull/68970
798//! [`min_spec...`]: https://github.com/rust-lang/rust/pull/68970 "min_specialization"
799//!
800//! [`examples/encode.rs`]: https://github.com/zheland/try-specialize/blob/v0.1.2/examples/encode.rs
801//!
802//! [`TypeId`]: std::any::TypeId
803//! [`TypeId::of`]: std::any::TypeId::of
804//! [`transmute`]: https://doc.rust-lang.org/std/mem/fn.transmute.html "std::mem::transmute"
805//! [`transmute_copy`]: std::mem::transmute_copy
806//! [`ptr::read`]: std::ptr::read
807//! [`PartialEq`]: std::cmp::PartialEq
808//! [`Eq`]: std::cmp::Eq
809//! [`Arc::eq`]: std::sync::Arc::eq "<std::sync::Arc as PartialEq>::eq"
810//! [`Arc<T>`]: std::sync::Arc
811//!
812//! [`try_new`]: Specialization::try_new
813//! [`try_new_static`]: Specialization::try_new_static
814//! [`try_new_ignore_lifetimes`]: Specialization::try_new_ignore_lifetimes
815//! [`rev`]: Specialization::rev
816//! [`map`]: Specialization::map
817//! [`specialize`]: Specialization::specialize
818//! [`specialize_ref`]: Specialization::specialize_ref
819//! [`specialize_mut`]: Specialization::specialize_mut
820//!
821//! [`WeakSpecialization`]: unreliable::WeakSpecialization
822//! [`try_new_if_lifetime_free_weak`]: unreliable::WeakSpecialization::try_new_if_lifetime_free_weak
823//!
824//! [`try_specialize`]: TrySpecialize::try_specialize
825//! [`try_specialize_ref`]: TrySpecialize::try_specialize_ref
826//! [`try_specialize_from`]: TrySpecialize::try_specialize_from
827//! [`try_specialize_from_ref`]: TrySpecialize::try_specialize_from_ref
828//! [`try_specialize_static`]: TrySpecialize::try_specialize_static
829//! [`try_specialize_ref_static`]: TrySpecialize::try_specialize_ref_static
830//! [`..._ignore_lifetimes`]: TrySpecialize::try_specialize_ignore_lifetimes
831//! [`..._ref_ignore_lifetimes`]: TrySpecialize::try_specialize_ref_ignore_lifetimes
832//! [`..._mut_ignore_lifetimes`]: TrySpecialize::try_specialize_mut_ignore_lifetimes
833//!
834//! [`TrySpecializeWeak`]: unreliable::TrySpecializeWeak
835//! [`..._if_lifetime_free_weak`]: unreliable::TrySpecializeWeak::try_specialize_if_lifetime_free_weak
836//! [`..._ref_if_lifetime_free_weak`]: unreliable::TrySpecializeWeak::try_specialize_ref_if_lifetime_free_weak
837//! [`..._mut_if_lifetime_free_weak`]: unreliable::TrySpecializeWeak::try_specialize_mut_if_lifetime_free_weak
838//!
839//! [`castaway`]: https://crates.io/crates/castaway
840//! [`syllogism`]: https://crates.io/crates/syllogism
841//! [`syllogism_macro`]: https://crates.io/crates/syllogism_macro
842//! [`specialized-dispatch`]: https://crates.io/crates/specialized-dispatch
843//! [`spec...ch`]: https://crates.io/crates/specialized-dispatch "specialized-dispatch"
844//! [`spez`]: https://crates.io/crates/spez
845//! [`coe-rs`]: https://crates.io/crates/coe-rs
846//! [`downcast-rs`]: https://crates.io/crates/downcast-rs
847//! [`impls`]: https://crates.io/crates/impls
848//!
849//! [Autoref-Based Specialization]: https://lukaskalbertodt.github.io/2019/12/05/generalized-autoref-based-specialization.html
850
851#[cfg(feature = "alloc")]
852extern crate alloc;
853
854#[cfg(feature = "std")]
855extern crate std;
856
857mod lifetime_free;
858mod specialization;
859mod try_specialize;
860mod type_eq;
861mod type_fn;
862
863#[cfg(all(feature = "alloc", feature = "unreliable"))]
864#[cfg_attr(docsrs, doc(cfg(all(feature = "alloc", feature = "unreliable"))))]
865pub mod unreliable;
866
867pub use lifetime_free::*;
868pub use specialization::*;
869pub use try_specialize::*;
870pub use type_eq::*;
871pub use type_fn::*;
872
873#[cfg(all(test, not(doc)))]
874mod integration_tests_deps {
875 use {paste as _, rustversion as _, version_sync as _};
876}
877
878#[cfg(all(test, not(doc)))]
879mod doc_tests_deps {
880 use {
881 castaway as _, coe as _, downcast_rs as _, hashbrown as _, specialize as _,
882 specialized_dispatch as _, syllogism as _, syllogism_macro as _,
883 };
884}
885
886#[doc(hidden)]
887#[cfg(all(feature = "alloc", feature = "unreliable"))]
888pub mod macro_deps {
889 pub use alloc::rc::Rc;
890}