try_specialize/unreliable/
try_specialize_weak.rs

1use crate::unreliable::WeakSpecialization;
2use crate::Specialization;
3
4/// A extension trait for [`TrySpecialize`] trait for specializing one
5/// completely unconstrained type to another completely unconstrained type.
6///
7/// This trait uses [`Specialization`] helper struct and [`WeakSpecialization`]
8/// helper trait to perform all conversions. You can use [`Specialization`] and
9/// [`WeakSpecialization`] directly if you need to perform more complex
10/// specialization cases or to cache the specializable ability.
11///
12/// # Reliability
13///
14/// While it is unlikely, there is still a possibility that the methods of this
15/// trait may return false negatives in future Rust versions.
16///
17/// The correctness of the results returned by the methods depends on the
18/// following:
19/// - Documented behavior that if `T` implements `Eq`, two `Rc`s that point to
20///   the same allocation are always equal:
21///   <https://doc.rust-lang.org/1.82.0/std/rc/struct.Rc.html#method.eq>.
22/// - Undocumented behavior that the `Rc::partial_eq` implementation for `T: Eq`
23///   will not use `PartialEq::eq` if both `Rc`s point to the same memory
24///   location.
25/// - The assumption that the undocumented short-circuit behavior described
26///   above will be retained for optimization purposes.
27///
28/// There is no formal guarantee that the undocumented behavior described above
29/// will be retained. If the implementation changes in a future Rust version,
30/// the function may return a false negative, that is, it may return `false`,
31/// even though `T` implements the trait. However, the implementation guarantees
32/// that a false positive result is impossible, i.e., the function will never
33/// return true if `T` does not implement the trait in any future Rust version.
34///
35/// Details:
36/// - <https://internals.rust-lang.org/t/rc-uses-visibly-behavior-changing-specialization-is-that-okay/16173/6>,
37/// - <https://users.rust-lang.org/t/hack-to-specialize-w-write-for-vec-u8/100366>,
38/// - <https://doc.rust-lang.org/1.82.0/std/rc/struct.Rc.html#method.eq>,
39/// - <https://github.com/rust-lang/rust/issues/42655>.
40///
41/// [`TrySpecialize`]: crate::TrySpecialize
42pub trait TrySpecializeWeak {
43    /// Attempts to specialize `Self` as `T` checking that underlying `Self`
44    /// type implements [`LifetimeFree`].
45    ///
46    /// Returns `T` as `Self` wrapped in `Ok` if `Self` and `T` types are
47    /// identical and [`impls_lifetime_free_weak::<Self>()`] check succeed.
48    /// Otherwise, it returns `T` wrapped in `Err`.
49    ///
50    /// The [`LifetimeFree`] trait is **not** automatically derived for all
51    /// lifetime-free types. The library only implements it for standard library
52    /// types that do not have any lifetime parameters. Prefer to specialize to
53    /// specific [`LifetimeFree`] type if possible with
54    /// [`TrySpecialize::try_specialize`].
55    ///
56    /// [`LifetimeFree`]: crate::LifetimeFree
57    /// [`TrySpecialize::try_specialize`]: crate::TrySpecialize::try_specialize
58    /// [`impls_lifetime_free_weak::<Self>()`]: crate::unreliable::impls_lifetime_free_weak
59    ///
60    /// # Examples
61    ///
62    /// ```rust
63    /// # #[cfg(all(feature = "alloc", feature = "unreliable"))] {
64    /// use core::ops::Add;
65    ///
66    /// use try_specialize::unreliable::TrySpecializeWeak;
67    ///
68    /// fn add_if_same_ty_weak<T1, T2>(left: T1, right: T2) -> Result<T1, (T1, T2)>
69    /// where
70    ///     T1: Add<Output = T1>,
71    /// {
72    ///     match right.try_specialize_if_lifetime_free_weak() {
73    ///         Ok(right) => Ok(left + right),
74    ///         Err(right) => Err((left, right)),
75    ///     }
76    /// }
77    ///
78    /// assert_eq!(add_if_same_ty_weak(42_u32, 123_u32), Ok(165));
79    /// assert_eq!(
80    ///     add_if_same_ty_weak(123.456_f64, 123_u32),
81    ///     Err((123.456_f64, 123_u32))
82    /// );
83    /// assert_eq!(
84    ///     add_if_same_ty_weak(123.456_f64, 1000.111_f64),
85    ///     Ok(1123.567_f64)
86    /// );
87    /// # }
88    #[expect(clippy::missing_errors_doc, reason = "already described")]
89    #[inline]
90    fn try_specialize_if_lifetime_free_weak<T>(self) -> Result<T, Self>
91    where
92        Self: Sized,
93    {
94        if let Some(spec) = Specialization::try_new_if_lifetime_free_weak() {
95            Ok(spec.specialize(self))
96        } else {
97            Err(self)
98        }
99    }
100
101    /// Attempts to specialize `&Self` as `&T` checking that underlying `Self`
102    /// type implements [`LifetimeFree`].
103    ///
104    /// The [`LifetimeFree`] trait is **not** automatically derived for all
105    /// lifetime-free types. The library only implements it for standard library
106    /// types that do not have any lifetime parameters. Prefer to specialize to
107    /// specific [`LifetimeFree`] type if possible with
108    /// [`TrySpecialize::try_specialize_ref`].
109    ///
110    /// [`LifetimeFree`]: crate::LifetimeFree
111    /// [`TrySpecialize::try_specialize_ref`]: crate::TrySpecialize::try_specialize_ref
112    /// [`impls_lifetime_free_weak::<Self>()`]: crate::unreliable::impls_lifetime_free_weak
113    ///
114    /// # Examples
115    ///
116    /// ```rust
117    /// # #[cfg(all(feature = "alloc", feature = "unreliable"))] {
118    /// use try_specialize::unreliable::TrySpecializeWeak;
119    ///
120    /// fn eq_if_same_ty_weak<T1, T2>(left: &T1, right: &T2) -> Option<bool>
121    /// where
122    ///     T1: PartialEq,
123    /// {
124    ///     right.
125    ///         try_specialize_ref_if_lifetime_free_weak().
126    ///         map(|right| left == right)
127    /// }
128    ///
129    /// assert_eq!(eq_if_same_ty_weak(&42_u32, &42_u32), Some(true));
130    /// assert_eq!(eq_if_same_ty_weak(&42_u32, &123_u32), Some(false));
131    /// assert_eq!(eq_if_same_ty_weak(&123.456_f64, &123_u32), None);
132    /// # }
133    #[inline]
134    fn try_specialize_ref_if_lifetime_free_weak<T>(&self) -> Option<&T>
135    where
136        T: ?Sized,
137    {
138        Specialization::try_new_if_lifetime_free_weak().map(|spec| spec.specialize_ref(self))
139    }
140
141    /// Attempts to specialize `&mut Self` as `&mut T` checking that underlying
142    /// `Self` type implements [`LifetimeFree`].
143    ///
144    /// The [`LifetimeFree`] trait is **not** automatically derived for all
145    /// lifetime-free types. The library only implements it for standard library
146    /// types that do not have any lifetime parameters. Prefer to specialize to
147    /// specific [`LifetimeFree`] type if possible with
148    /// [`TrySpecialize::try_specialize_mut`].
149    ///
150    /// [`LifetimeFree`]: crate::LifetimeFree
151    /// [`TrySpecialize::try_specialize_mut`]: crate::TrySpecialize::try_specialize_mut
152    /// [`impls_lifetime_free_weak::<Self>()`]: crate::unreliable::impls_lifetime_free_weak
153    ///
154    /// # Examples
155    ///
156    /// ```rust
157    /// # #[cfg(all(feature = "alloc", feature = "unreliable"))] {
158    /// use core::ops::AddAssign;
159    ///
160    /// use try_specialize::unreliable::TrySpecializeWeak;
161    ///
162    /// fn transfer_if_same_ty_weak<T1, T2>(left: &mut T1, right: &mut T2) -> bool
163    /// where
164    ///     T1: AddAssign + Default,
165    /// {
166    ///     match right.try_specialize_mut_if_lifetime_free_weak() {
167    ///         Some(right) => {
168    ///             *left += core::mem::take(right);
169    ///             true
170    ///         },
171    ///         None => false,
172    ///     }
173    /// }
174    ///
175    /// let mut v1: u32 = 10;
176    /// let mut v2: f64 = 20.0;
177    /// let mut v3: u32 = 40;
178    /// let mut v4: f64 = 80.0;
179    ///
180    /// assert_eq!(transfer_if_same_ty_weak(&mut v1, &mut v2), false);
181    /// assert_eq!((v1, v2, v3, v4), (10, 20.0, 40, 80.0));
182    /// assert_eq!(transfer_if_same_ty_weak(&mut v1, &mut v3), true);
183    /// assert_eq!((v1, v2, v3, v4), (50, 20.0, 0, 80.0));
184    /// assert_eq!(transfer_if_same_ty_weak(&mut v1, &mut v4), false);
185    /// assert_eq!((v1, v2, v3, v4), (50, 20.0, 0, 80.0));
186    /// assert_eq!(transfer_if_same_ty_weak(&mut v2, &mut v3), false);
187    /// assert_eq!((v1, v2, v3, v4), (50, 20.0, 0, 80.0));
188    /// assert_eq!(transfer_if_same_ty_weak(&mut v2, &mut v4), true);
189    /// assert_eq!((v1, v2, v3, v4), (50, 100.0, 0, 0.0));
190    /// # }
191    #[inline]
192    fn try_specialize_mut_if_lifetime_free_weak<T>(&mut self) -> Option<&mut T>
193    where
194        T: ?Sized,
195    {
196        Specialization::try_new_if_lifetime_free_weak().map(|spec| spec.specialize_mut(self))
197    }
198}
199
200impl<T> TrySpecializeWeak for T where T: ?Sized {}
201
202#[cfg(test)]
203mod tests {
204    #[cfg(feature = "alloc")]
205    use crate::unreliable::TrySpecializeWeak;
206
207    #[cfg(feature = "alloc")]
208    #[test]
209    fn test_try_specialize_if_lifetime_free() {
210        fn try_spec_erased<T1, T2>(value: T1) -> Result<T2, T1> {
211            value.try_specialize_if_lifetime_free_weak()
212        }
213
214        assert_eq!(try_spec_erased::<_, u32>(123_u32), Ok(123_u32));
215        assert_eq!(try_spec_erased::<_, i32>(123_u32), Err(123_u32));
216
217        assert_eq!(try_spec_erased::<_, u32>("abc"), Err("abc"));
218        // '&'static str' is not [`LifetimeFree`] so the specialization failed
219        // as expected even if the types are equal.
220        assert_eq!(try_spec_erased::<_, &'static str>("abc"), Err("abc"));
221    }
222}