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