Skip to main content

num_conv/
lib.rs

1//! `num_conv` is a crate to convert between integer types without using `as` casts. This provides
2//! better certainty when refactoring, makes the exact behavior of code more explicit, and allows
3//! using turbofish syntax. The crate is currently in the process of being uplifted into the
4//! standard library; see [rust-lang/rust#154330](https://github.com/rust-lang/rust/issues/154330)
5//! for details.
6
7#![no_std]
8
9/// Anonymously import all extension traits.
10///
11/// This allows you to use the methods without worrying about polluting the namespace or importing
12/// them individually.
13///
14/// ```rust
15/// use num_conv::prelude::*;
16/// ```
17pub mod prelude {
18    #[allow(deprecated)]
19    pub use crate::{Extend as _, Truncate as _, Widen as _};
20}
21
22mod sealed {
23    pub trait Integer {}
24
25    macro_rules! impl_integer {
26        ($($t:ty)*) => {$(
27            impl Integer for $t {}
28        )*};
29    }
30
31    impl Integer for isize {}impl_integer! {
32        u8 u16 u32 u64 u128 usize
33        i8 i16 i32 i64 i128 isize
34    }
35
36    #[deprecated(since = "0.2.2", note = "use `WidenTargetSealed` instead")]
37    pub trait ExtendTargetSealed<T> {
38        #[deprecated(since = "0.2.2", note = "use `widen` instead")]
39        fn extend(self) -> T;
40    }
41
42    pub trait WidenTargetSealed<T> {
43        fn widen(self) -> T;
44    }
45
46    pub trait TruncateTargetSealed<T> {
47        fn truncate(self) -> T;
48        fn saturating_truncate(self) -> T;
49        fn checked_truncate(self) -> Option<T>;
50    }
51}
52
53/// A type that can be used with turbofish syntax in [`Extend::extend`].
54///
55/// It is unlikely that you will want to use this trait directly. You are probably looking for the
56/// [`Extend`] trait.
57#[deprecated(since = "0.2.2", note = "use `WidenTarget` instead")]
58#[allow(deprecated)]
59pub trait ExtendTarget<T>: sealed::ExtendTargetSealed<T> {}
60
61/// A type that can be used with turbofish syntax in [`Widen::widen`].
62///
63/// It is unlikely that you will want to use this trait directly. You are probably looking for the
64/// [`Widen`] trait.
65pub trait WidenTarget<T>: sealed::WidenTargetSealed<T> {}
66
67/// A type that can be used with turbofish syntax in [`Truncate::truncate`].
68///
69/// It is unlikely that you will want to use this trait directly. You are probably looking for the
70/// [`Truncate`] trait.
71pub trait TruncateTarget<T>: sealed::TruncateTargetSealed<T> {}
72
73/// Widen to an integer of the same size or larger, preserving its value.
74///
75/// ```rust
76/// # use num_conv::Widen;
77/// assert_eq!(0_u8.widen::<u16>(), 0_u16);
78/// assert_eq!(0_u16.widen::<u32>(), 0_u32);
79/// assert_eq!(0_u32.widen::<u64>(), 0_u64);
80/// assert_eq!(0_u64.widen::<u128>(), 0_u128);
81/// ```
82///
83/// ```rust
84/// # use num_conv::Widen;
85/// assert_eq!((-1_i8).widen::<i16>(), -1_i16);
86/// assert_eq!((-1_i16).widen::<i32>(), -1_i32);
87/// assert_eq!((-1_i32).widen::<i64>(), -1_i64);
88/// assert_eq!((-1_i64).widen::<i128>(), -1_i128);
89/// ```
90pub trait Widen: sealed::Integer {
91    /// Widen an integer to an integer of the same size or larger, preserving its value.
92    fn widen<T>(self) -> T
93    where
94        Self: WidenTarget<T>;
95}
96
97impl<T: sealed::Integer> Widen for T {
98    fn widen<U>(self) -> U
99    where
100        T: WidenTarget<U>,
101    {
102        sealed::WidenTargetSealed::widen(self)
103    }
104}
105
106/// Extend to an integer of the same size or larger, preserving its value.
107///
108/// ```rust
109/// # use num_conv::Extend;
110/// assert_eq!(0_u8.extend::<u16>(), 0_u16);
111/// assert_eq!(0_u16.extend::<u32>(), 0_u32);
112/// assert_eq!(0_u32.extend::<u64>(), 0_u64);
113/// assert_eq!(0_u64.extend::<u128>(), 0_u128);
114/// ```
115///
116/// ```rust
117/// # use num_conv::Extend;
118/// assert_eq!((-1_i8).extend::<i16>(), -1_i16);
119/// assert_eq!((-1_i16).extend::<i32>(), -1_i32);
120/// assert_eq!((-1_i32).extend::<i64>(), -1_i64);
121/// assert_eq!((-1_i64).extend::<i128>(), -1_i128);
122/// ```
123#[deprecated(since = "0.2.2", note = "use `Widen` instead")]
124#[allow(deprecated)]
125pub trait Extend: sealed::Integer {
126    /// Extend an integer to an integer of the same size or larger, preserving its value.
127    fn extend<T>(self) -> T
128    where
129        Self: ExtendTarget<T>;
130}
131
132#[allow(deprecated)]
133impl<T: sealed::Integer> Extend for T {
134    fn extend<U>(self) -> U
135    where
136        T: ExtendTarget<U>,
137    {
138        sealed::ExtendTargetSealed::extend(self)
139    }
140}
141
142/// Truncate to an integer of the same size or smaller.
143///
144/// Preserve the least significant bits with `.truncate()`:
145///
146/// ```rust
147/// # use num_conv::Truncate;
148/// assert_eq!(u16::MAX.truncate::<u8>(), u8::MAX);
149/// assert_eq!(u32::MAX.truncate::<u16>(), u16::MAX);
150/// assert_eq!(u64::MAX.truncate::<u32>(), u32::MAX);
151/// assert_eq!(u128::MAX.truncate::<u64>(), u64::MAX);
152/// ```
153///
154/// ```rust
155/// # use num_conv::Truncate;
156/// assert_eq!((-1_i16).truncate::<i8>(), -1_i8);
157/// assert_eq!((-1_i32).truncate::<i16>(), -1_i16);
158/// assert_eq!((-1_i64).truncate::<i32>(), -1_i32);
159/// assert_eq!((-1_i128).truncate::<i64>(), -1_i64);
160/// ```
161///
162/// Saturate to the numeric bounds with `.saturating_truncate()`:
163///
164/// ```rust
165/// # use num_conv::Truncate;
166/// assert_eq!(500_u16.saturating_truncate::<u8>(), u8::MAX);
167/// assert_eq!(u32::MAX.saturating_truncate::<u16>(), u16::MAX);
168/// assert_eq!(u64::MAX.saturating_truncate::<u32>(), u32::MAX);
169/// assert_eq!(u128::MAX.saturating_truncate::<u64>(), u64::MAX);
170/// ```
171///
172/// ```rust
173/// # use num_conv::Truncate;
174/// assert_eq!((-500_i16).saturating_truncate::<i8>(), i8::MIN);
175/// assert_eq!(i32::MIN.saturating_truncate::<i16>(), i16::MIN);
176/// assert_eq!(i64::MIN.saturating_truncate::<i32>(), i32::MIN);
177/// assert_eq!(i128::MIN.saturating_truncate::<i64>(), i64::MIN);
178/// ```
179///
180/// Checked with `.checked_truncate()`, returning `None` if the value is out of range:
181///
182/// ```rust
183/// # use num_conv::Truncate;
184/// assert_eq!(u16::MAX.checked_truncate::<u8>(), None);
185/// assert_eq!(u32::MAX.checked_truncate::<u16>(), None);
186/// assert_eq!(u64::MAX.checked_truncate::<u32>(), None);
187/// assert_eq!(u128::MAX.checked_truncate::<u64>(), None);
188/// ```
189///
190/// ```rust
191/// # use num_conv::Truncate;
192/// assert_eq!(i16::MIN.checked_truncate::<i8>(), None);
193/// assert_eq!(i32::MIN.checked_truncate::<i16>(), None);
194/// assert_eq!(i64::MIN.checked_truncate::<i32>(), None);
195/// assert_eq!(i128::MIN.checked_truncate::<i64>(), None);
196/// ```
197pub trait Truncate: sealed::Integer {
198    /// Truncate an integer to an integer of the same size or smaller, preserving the least
199    /// significant bits.
200    fn truncate<T>(self) -> T
201    where
202        Self: TruncateTarget<T>;
203
204    /// Truncate an integer to an integer of the same size or smaller, saturating to the numeric
205    /// bounds instead of wrapping.
206    fn saturating_truncate<T>(self) -> T
207    where
208        Self: TruncateTarget<T>;
209
210    /// Truncate an integer to an integer of the same size or smaller, returning `None` if the value
211    /// is out of range.
212    fn checked_truncate<T>(self) -> Option<T>
213    where
214        Self: TruncateTarget<T>;
215}
216
217impl<T: sealed::Integer> Truncate for T {
218    fn truncate<U>(self) -> U
219    where
220        T: TruncateTarget<U>,
221    {
222        sealed::TruncateTargetSealed::truncate(self)
223    }
224
225    fn saturating_truncate<U>(self) -> U
226    where
227        T: TruncateTarget<U>,
228    {
229        sealed::TruncateTargetSealed::saturating_truncate(self)
230    }
231
232    fn checked_truncate<U>(self) -> Option<U>
233    where
234        T: TruncateTarget<U>,
235    {
236        sealed::TruncateTargetSealed::checked_truncate(self)
237    }
238}
239
240macro_rules! impl_widen {
241    ($($from:ty => $($to:ty),+;)*) => {$($(
242        const _: () = assert!(
243            core::mem::size_of::<$from>() <= core::mem::size_of::<$to>(),
244            concat!(
245                "cannot widen ",
246                stringify!($from),
247                " to ",
248                stringify!($to),
249                " because ",
250                stringify!($from),
251                " is larger than ",
252                stringify!($to)
253            )
254        );
255
256        #[allow(deprecated)]
257        impl sealed::ExtendTargetSealed<$to> for $from {
258            #[inline]
259            fn extend(self) -> $to {
260                self as _
261            }
262        }
263
264        impl sealed::WidenTargetSealed<$to> for $from {
265            #[inline]
266            fn widen(self) -> $to {
267                self as _
268            }
269        }
270
271        #[allow(deprecated)]
272        impl ExtendTarget<$to> for $from {}
273        impl WidenTarget<$to> for $from {}
274    )+)*};
275}
276
277macro_rules! impl_truncate {
278    ($($($from:ty),+ => $to:ty;)*) => {$($(
279        const _: () = assert!(
280            core::mem::size_of::<$from>() >= core::mem::size_of::<$to>(),
281            concat!(
282                "cannot truncate ",
283                stringify!($from),
284                " to ",
285                stringify!($to),
286                " because ",
287                stringify!($from),
288                " is smaller than ",
289                stringify!($to)
290            )
291        );
292
293        impl sealed::TruncateTargetSealed<$to> for $from {
294            #[inline]
295            fn truncate(self) -> $to {
296                self as _
297            }
298
299            #[inline]
300            fn saturating_truncate(self) -> $to {
301                if self > <$to>::MAX as _ {
302                    <$to>::MAX
303                } else if self < <$to>::MIN as _ {
304                    <$to>::MIN
305                } else {
306                    self as _
307                }
308            }
309
310            #[inline]
311            fn checked_truncate(self) -> Option<$to> {
312                if self > <$to>::MAX as _ || self < <$to>::MIN as _ {
313                    None
314                } else {
315                    Some(self as _)
316                }
317            }
318        }
319
320        impl TruncateTarget<$to> for $from {}
321    )+)*};
322}
323
324const _: () =
    if !(core::mem::size_of::<isize>() <= core::mem::size_of::<isize>()) {
        {
            ::core::panicking::panic_fmt(format_args!("cannot widen isize to isize because isize is larger than isize"));
        }
    };
#[allow(deprecated)]
impl sealed::ExtendTargetSealed<isize> for isize {
    #[inline]
    fn extend(self) -> isize { self as _ }
}
impl sealed::WidenTargetSealed<isize> for isize {
    #[inline]
    fn widen(self) -> isize { self as _ }
}
#[allow(deprecated)]
impl ExtendTarget<isize> for isize { }
impl WidenTarget<isize> for isize {}impl_widen! {
325    u8 => u8, u16, u32, u64, u128, usize;
326    u16 => u16, u32, u64, u128, usize;
327    u32 => u32, u64, u128;
328    u64 => u64, u128;
329    u128 => u128;
330    usize => usize;
331
332    i8 => i8, i16, i32, i64, i128, isize;
333    i16 => i16, i32, i64, i128, isize;
334    i32 => i32, i64, i128;
335    i64 => i64, i128;
336    i128 => i128;
337    isize => isize;
338}
339
340const _: () =
    if !(core::mem::size_of::<isize>() >= core::mem::size_of::<isize>()) {
        {
            ::core::panicking::panic_fmt(format_args!("cannot truncate isize to isize because isize is smaller than isize"));
        }
    };
impl sealed::TruncateTargetSealed<isize> for isize {
    #[inline]
    fn truncate(self) -> isize { self as _ }
    #[inline]
    fn saturating_truncate(self) -> isize {
        if self > <isize>::MAX as _ {
            <isize>::MAX
        } else if self < <isize>::MIN as _ { <isize>::MIN } else { self as _ }
    }
    #[inline]
    fn checked_truncate(self) -> Option<isize> {
        if self > <isize>::MAX as _ || self < <isize>::MIN as _ {
            None
        } else { Some(self as _) }
    }
}
impl TruncateTarget<isize> for isize {}impl_truncate! {
341    u8, u16, u32, u64, u128, usize => u8;
342    u16, u32, u64, u128, usize => u16;
343    u32, u64, u128 => u32;
344    u64, u128 => u64;
345    u128 => u128;
346    usize => usize;
347
348    i8, i16, i32, i64, i128, isize => i8;
349    i16, i32, i64, i128, isize => i16;
350    i32, i64, i128 => i32;
351    i64, i128 => i64;
352    i128 => i128;
353    isize => isize;
354}