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}