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! {
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    }
40}
41
42/// A type that can be used with turbofish syntax in [`Extend::extend`].
43///
44/// It is unlikely that you will want to use this trait directly. You are probably looking for the
45/// [`Extend`] trait.
46pub trait ExtendTarget<T>: sealed::ExtendTargetSealed<T> {}
47
48/// A type that can be used with turbofish syntax in [`Truncate::truncate`].
49///
50/// It is unlikely that you will want to use this trait directly. You are probably looking for the
51/// [`Truncate`] trait.
52pub trait TruncateTarget<T>: sealed::TruncateTargetSealed<T> {}
53
54/// Extend to an integer of the same size or larger, preserving its value.
55///
56/// ```rust
57/// # use num_conv::Extend;
58/// assert_eq!(0_u8.extend::<u16>(), 0_u16);
59/// assert_eq!(0_u16.extend::<u32>(), 0_u32);
60/// assert_eq!(0_u32.extend::<u64>(), 0_u64);
61/// assert_eq!(0_u64.extend::<u128>(), 0_u128);
62/// ```
63///
64/// ```rust
65/// # use num_conv::Extend;
66/// assert_eq!((-1_i8).extend::<i16>(), -1_i16);
67/// assert_eq!((-1_i16).extend::<i32>(), -1_i32);
68/// assert_eq!((-1_i32).extend::<i64>(), -1_i64);
69/// assert_eq!((-1_i64).extend::<i128>(), -1_i128);
70/// ```
71pub trait Extend: sealed::Integer {
72    /// Extend an integer to an integer of the same size or larger, preserving its value.
73    fn extend<T>(self) -> T
74    where
75        Self: ExtendTarget<T>;
76}
77
78impl<T: sealed::Integer> Extend for T {
79    fn extend<U>(self) -> U
80    where
81        T: ExtendTarget<U>,
82    {
83        sealed::ExtendTargetSealed::extend(self)
84    }
85}
86
87/// Truncate to an integer of the same size or smaller, preserving the least significant bits.
88///
89/// ```rust
90/// # use num_conv::Truncate;
91/// assert_eq!(u16::MAX.truncate::<u8>(), u8::MAX);
92/// assert_eq!(u32::MAX.truncate::<u16>(), u16::MAX);
93/// assert_eq!(u64::MAX.truncate::<u32>(), u32::MAX);
94/// assert_eq!(u128::MAX.truncate::<u64>(), u64::MAX);
95/// ```
96///
97/// ```rust
98/// # use num_conv::Truncate;
99/// assert_eq!((-1_i16).truncate::<i8>(), -1_i8);
100/// assert_eq!((-1_i32).truncate::<i16>(), -1_i16);
101/// assert_eq!((-1_i64).truncate::<i32>(), -1_i32);
102/// assert_eq!((-1_i128).truncate::<i64>(), -1_i64);
103/// ```
104pub trait Truncate: sealed::Integer {
105    /// Truncate an integer to an integer of the same size or smaller, preserving the least
106    /// significant bits.
107    fn truncate<T>(self) -> T
108    where
109        Self: TruncateTarget<T>;
110}
111
112impl<T: sealed::Integer> Truncate for T {
113    fn truncate<U>(self) -> U
114    where
115        T: TruncateTarget<U>,
116    {
117        sealed::TruncateTargetSealed::truncate(self)
118    }
119}
120
121macro_rules! impl_extend {
122    ($($from:ty => $($to:ty),+;)*) => {$($(
123        const _: () = assert!(
124            core::mem::size_of::<$from>() <= core::mem::size_of::<$to>(),
125            concat!(
126                "cannot extend ",
127                stringify!($from),
128                " to ",
129                stringify!($to),
130                " because ",
131                stringify!($from),
132                " is larger than ",
133                stringify!($to)
134            )
135        );
136
137        impl sealed::ExtendTargetSealed<$to> for $from {
138            fn extend(self) -> $to {
139                self as _
140            }
141        }
142
143        impl ExtendTarget<$to> for $from {}
144    )+)*};
145}
146
147macro_rules! impl_truncate {
148    ($($($from:ty),+ => $to:ty;)*) => {$($(
149        const _: () = assert!(
150            core::mem::size_of::<$from>() >= core::mem::size_of::<$to>(),
151            concat!(
152                "cannot truncate ",
153                stringify!($from),
154                " to ",
155                stringify!($to),
156                " because ",
157                stringify!($from),
158                " is smaller than ",
159                stringify!($to)
160            )
161        );
162
163        impl sealed::TruncateTargetSealed<$to> for $from {
164            fn truncate(self) -> $to {
165                self as _
166            }
167        }
168
169        impl TruncateTarget<$to> for $from {}
170    )+)*};
171}
172
173impl_extend! {
174    u8 => u8, u16, u32, u64, u128, usize;
175    u16 => u16, u32, u64, u128, usize;
176    u32 => u32, u64, u128;
177    u64 => u64, u128;
178    u128 => u128;
179    usize => usize;
180
181    i8 => i8, i16, i32, i64, i128, isize;
182    i16 => i16, i32, i64, i128, isize;
183    i32 => i32, i64, i128;
184    i64 => i64, i128;
185    i128 => i128;
186    isize => isize;
187}
188
189impl_truncate! {
190    u8, u16, u32, u64, u128, usize => u8;
191    u16, u32, u64, u128, usize => u16;
192    u32, u64, u128 => u32;
193    u64, u128 => u64;
194    u128 => u128;
195    usize => usize;
196
197    i8, i16, i32, i64, i128, isize => i8;
198    i16, i32, i64, i128, isize => i16;
199    i32, i64, i128 => i32;
200    i64, i128 => i64;
201    i128 => i128;
202    isize => isize;
203}