cadd/convert.rs
1//! Converting values to another type.
2
3/// Extention trait that enables `.into_type::<T>()` syntax. Also works for
4/// [`cinto`](Cinto),
5/// [`try_into`](TryInto),
6/// [`saturating_into`](SaturatingInto).
7///
8/// When you replace unchecked type casts (e.g. `number as u32`) with an infallible conversion
9/// (`number.into()`) or a fallible conversion (`number.try_into()?`), you may often encounter
10/// type inference errors if the context doesn't have enough information about the target type:
11/// ```
12/// fn f1(input: u32) -> u64 {
13/// 10 + (input as u64) // Compiles
14/// }
15/// ```
16/// ```compile_fail
17/// fn f1(input: u32) -> Result<u64, std::num::TryFromIntError> {
18/// let a = 10 + input.try_into()?; // Doesn't compile
19/// Ok(a)
20/// }
21/// ```
22/// The easiest way to solve it in `std` is to use `From` or `TryFrom` instead
23/// so that you can specify the target type:
24/// ```
25/// fn f1(input: u32) -> Result<u64, std::num::TryFromIntError> {
26/// let a = 10 + u64::try_from(input)?; // Compiles
27/// Ok(a)
28/// }
29/// ```
30/// This can cause unnecessary friction because it requires some rearrangement of the code and reduces its
31/// readability. The `IntoType` trait provides an alternative way to do it:
32/// ```
33/// use cadd::convert::IntoType;
34/// fn f1(input: u32) -> Result<u64, std::num::TryFromIntError> {
35/// let a = 10 + input.try_into_type::<u64>()?; // Compiles
36/// Ok(a)
37/// }
38/// ```
39///
40/// This trait is implemented for all types. However, each method has its own type bound that requires
41/// the corresponding conversion trait to be implemented.
42pub trait IntoType {
43 /// An alternative to [`.into()`](core::convert::Into) that allows specifying the target type.
44 /// ```
45 /// use cadd::convert::IntoType;
46 /// assert_eq!(2u32.into_type::<u64>(), 2);
47 /// ```
48 #[inline]
49 fn into_type<T>(self) -> T
50 where
51 Self: Into<T>,
52 {
53 self.into()
54 }
55
56 /// An alternative to [`.try_into()`](core::convert::TryInto) that allows specifying the target type.
57 /// ```
58 /// use cadd::convert::IntoType;
59 /// assert!((-2i32).try_into_type::<u32>().is_err());
60 /// assert_eq!(2i32.try_into_type::<u32>().unwrap(), 2);
61 /// ```
62 #[inline]
63 fn try_into_type<T>(self) -> Result<T, Self::Error>
64 where
65 Self: TryInto<T>,
66 {
67 self.try_into()
68 }
69
70 /// An alternative to [`.cinto()`](Cinto) that allows specifying the target type.
71 /// ```
72 /// use cadd::convert::IntoType;
73 /// assert!((-2i32).cinto_type::<u32>().is_err());
74 /// assert_eq!(2i32.cinto_type::<u32>().unwrap(), 2);
75 /// ```
76 #[inline]
77 fn cinto_type<T>(self) -> Result<T, Self::Error>
78 where
79 Self: Cinto<T>,
80 {
81 self.cinto()
82 }
83
84 /// An alternative to [`.saturating_into()`](SaturatingInto) that allows specifying the target type.
85 /// ```
86 /// use cadd::convert::IntoType;
87 /// assert_eq!(300_u32.saturating_into_type::<u8>(), 255);
88 /// ```
89 #[inline]
90 fn saturating_into_type<T>(self) -> T
91 where
92 Self: SaturatingInto<T>,
93 {
94 self.saturating_into()
95 }
96}
97
98impl<T: ?Sized> IntoType for T {}
99
100/// Checked conversion from `Input` to `Self`.
101///
102/// This is semantically the same as [`TryFrom`]. However, `Cfrom`
103/// aims to provide a rich error message, as opposed to many implementations of `TryFrom` in `std`
104/// that provide minimal informations in errors.
105///
106/// [`Cinto`] trait provides an alternative way to do the same conversion.
107/// Similar to `TryFrom`, it's recommended to always implement `Cfrom` instead of [`Cinto`].
108/// The corresponding `Cinto` implementation will be covered by the blanket impl.
109///
110/// # Comparison with `std` alternatives
111/// ```
112/// # fn test1() -> Result<(), Box<dyn std::error::Error>> {
113/// let a: i32 = -50;
114/// // Reinterpretation cast: never fails, but produces a different value
115/// // when the input value is out of bounds.
116/// let b = a as u32;
117///
118/// // Returns an uninformative error: `TryFromIntError(())`;
119/// // requires type annotation on the left side.
120/// let b2: u32 = a.try_into()?;
121///
122/// // Returns an informative error:
123/// // `failed to convert value -50 from i32 to u32: value is out of bounds`.
124/// // Still requires type annotation on the left side.
125/// use cadd::convert::Cinto;
126///
127/// let b3: u32 = a.cinto()?;
128///
129/// // Same error as above, and output type can be specified in the call.
130/// use cadd::convert::IntoType;
131///
132/// let b4 = a.cinto_type::<u32>()?;
133/// # Ok(())
134/// # }
135/// # fn main() {
136/// # test1().unwrap_err();
137/// # }
138/// ```
139///
140/// # Notable implementations
141///
142/// * Fallible conversions between integer types:
143/// ```
144/// use cadd::convert::{Cfrom, Cinto, IntoType};
145///
146/// // Output type can be inferred from context:
147/// let a = 15_i32;
148/// let b: u32 = a.cinto().unwrap();
149/// assert_eq!(b, 15);
150///
151/// // It's also possible to specify output type explicitly:
152/// let c = a.cinto_type::<u32>().unwrap();
153/// assert_eq!(c, 15);
154///
155/// // The error contains the input value, input type and output type:
156/// let d = -15_i32;
157/// let err = d.cinto_type::<u32>().unwrap_err();
158/// assert!(err.to_string().contains(
159/// "failed to convert value -15 from i32 to u32: value is out of bounds"
160/// ));
161///
162/// // Using `Cfrom` directly:
163/// let e = u32::cfrom(a).unwrap();
164/// assert_eq!(e, 15);
165/// ```
166/// * Conversions from slices to fixed arrays:
167/// ```
168/// # use cadd::convert::{Cfrom, Cinto, IntoType};
169/// let a: &[u32] = &[1, 2, 3, 4];
170/// let b: &[u32; 4] = a.cinto().unwrap();
171/// assert!(
172/// a.cinto_type::<&[u32; 5]>().unwrap_err().to_string().contains(
173/// "expected 5 items, got [1, 2, 3, 4] (4 items)"
174/// )
175/// );
176/// ```
177/// * Conversions from `CString`, `&CStr`, `OsString`, `&OsStr`, `PathBuf`, `&Path` to `String` and `&str`:
178/// ```
179/// # use {cadd::convert::{Cfrom, Cinto, IntoType}, std::ffi::CString};
180/// let a = CString::from_vec_with_nul(vec![104, 101, 108, 108, 111, 0]).unwrap();
181/// assert_eq!(a.cinto_type::<String>().unwrap(), "hello");
182///
183/// let a2 = CString::from_vec_with_nul(vec![104, 128, 108, 108, 111, 0]).unwrap();
184/// assert!(
185/// a2.clone().cinto_type::<String>().unwrap_err().to_string().contains(
186/// "failed to convert bytes to string: \
187/// invalid utf-8 sequence of 1 bytes from index 1; \
188/// input: [104, 128, 108, 108, 111] (5 bytes); \
189/// input as lossy utf-8: \"h�llo\""
190/// )
191/// );
192/// ```
193pub trait Cfrom<Input>: Sized {
194 #[expect(missing_docs, reason = "no need for doc")]
195 type Error;
196 /// Attempts to convert `from` into `Self`.
197 fn cfrom(from: Input) -> Result<Self, Self::Error>;
198}
199
200/// Checked conversion from `Self` to `Output`.
201///
202/// This trait is automatically implemented when `Output` implements <code>[Cfrom]<Self></code>.
203///
204/// In order to help with type inference,
205/// the [`IntoType`] extension trait provides `.cinto_type::<T>()` syntax.
206///
207/// **See [`Cfrom`] for main documentation.**
208pub trait Cinto<Output>: Sized {
209 #[expect(missing_docs, reason = "no need for doc")]
210 type Error;
211 /// Attempts to convert `self` into `Output`.
212 fn cinto(self) -> Result<Output, Self::Error>;
213}
214
215impl<Input, Output> Cinto<Output> for Input
216where
217 Output: Cfrom<Input>,
218{
219 type Error = <Output as Cfrom<Input>>::Error;
220 #[inline]
221 fn cinto(self) -> Result<Output, Self::Error> {
222 Output::cfrom(self)
223 }
224}
225
226/// Saturating conversion of a number from `Input` to `Self`.
227///
228/// If the value being converted is out of bounds for the target type,
229/// the closest representable value is returned. Consequently, if the value is out of bounds,
230/// this conversion always returns `Self::MIN` or `Self::MAX`.
231/// ```
232/// use cadd::convert::SaturatingFrom;
233///
234/// assert_eq!(u8::saturating_from(300_u32), 255);
235/// assert_eq!(u8::saturating_from(200_u32), 200);
236/// assert_eq!(u8::saturating_from(-300_i32), 0);
237/// assert_eq!(i8::saturating_from(-300_i32), -128);
238/// ```
239/// [`SaturatingInto`] trait provides an alternative way to do the same conversion.
240/// Similar to [`TryFrom`], it's recommended to always implement
241/// `SaturatingFrom` instead of [`SaturatingInto`](Cinto).
242/// The corresponding `SaturatingInto` implementation will be covered by the blanket impl.
243///
244/// In order to help with type inference,
245/// the [`IntoType`] extension trait provides `.saturating_into_type::<T>()` syntax.
246pub trait SaturatingFrom<Input>: Sized {
247 /// Returns the value of `Self` type that is the closest to `from`.
248 fn saturating_from(from: Input) -> Self;
249}
250
251/// Saturating conversion of a number from `Self` to `Output`.
252///
253/// This trait is automatically implemented when `Output` implements <code>[SaturatingFrom]<Self></code>.
254/// Similar to [`TryInto`], it's recommended to always implement
255/// `SaturatingFrom` instead of [`SaturatingInto`](Cinto).
256///
257/// In order to help with type inference,
258/// the [`IntoType`] extension trait provides `.saturating_into_type::<T>()` syntax.
259///
260/// **See [`SaturatingFrom`] for main documentation.**
261///
262/// # Examples
263/// ```
264/// use cadd::convert::{SaturatingInto, IntoType};
265///
266/// let v: u8 = 300_u32.saturating_into();
267/// assert_eq!(v, 255);
268/// // Or with `IntoType` extension trait:
269/// assert_eq!(300_u32.saturating_into_type::<u8>(), 255);
270///
271/// // More examples:
272/// assert_eq!(200_u32.saturating_into_type::<u8>(), 200);
273/// assert_eq!((-300_i32).saturating_into_type::<u8>(), 0);
274/// assert_eq!((-300_i32).saturating_into_type::<i8>(), -128);
275/// ```
276pub trait SaturatingInto<Output>: Sized {
277 /// Returns the value of `Output` type that is the closest to `self`.
278 fn saturating_into(self) -> Output;
279}
280
281impl<Input, Output> SaturatingInto<Output> for Input
282where
283 Output: SaturatingFrom<Input>,
284{
285 #[inline]
286 fn saturating_into(self) -> Output {
287 Output::saturating_from(self)
288 }
289}
290
291/// Conversion from an integer type to the corresponding [`NonZero`](core::num::NonZero) type.
292///
293/// If the value is zero, it returns an error with a backtrace.
294///
295/// [`non_zero`] provides an alternative way to do the same conversion.
296///
297/// # Examples
298/// ```
299/// use cadd::convert::ToNonZero;
300/// use std::num::NonZero;
301///
302/// let x: u32 = 5;
303/// let y = x.to_non_zero().unwrap();
304/// assert_eq!(y, NonZero::new(5).unwrap());
305///
306/// let x2: u32 = 0;
307/// assert!(
308/// x2.to_non_zero().unwrap_err().to_string().contains("unexpected zero value")
309/// );
310/// ```
311pub trait ToNonZero {
312 #[expect(missing_docs, reason = "no need for doc")]
313 type Error;
314 /// Output type, usually equal to `NonZero<Self>`.
315 type NonZero;
316 /// Returns `NonZero` value equal to `self`, returning an error if `self` is 0.
317 fn to_non_zero(self) -> Result<Self::NonZero, Self::Error>;
318}
319
320/// Returns [`NonZero`](core::num::NonZero) value equal to `value`, returning an error if `value` is 0.
321///
322/// See also: [`ToNonZero`].
323#[inline]
324pub fn non_zero<T: ToNonZero>(value: T) -> crate::Result<T::NonZero, T::Error> {
325 value.to_non_zero()
326}
327
328macro_rules! impl_to_non_zero {
329 ($($ty:ident,)*) => {
330 $(
331 impl $crate::convert::ToNonZero for $ty {
332 type Error = $crate::Error;
333 type NonZero = ::core::num::NonZero<$ty>;
334 #[inline]
335 fn to_non_zero(self) -> $crate::Result<Self::NonZero> {
336 ::core::num::NonZero::new(self).ok_or_else(|| $crate::Error::new("unexpected zero value".into()))
337 }
338 }
339 )*
340 }
341}
342
343impl_to_non_zero!(u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize,);