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 | /// ``` |
15 | pub mod prelude { |
16 | pub use crate::{CastSigned as _, CastUnsigned as _, Extend as _, Truncate as _}; |
17 | } |
18 | |
19 | mod 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 | /// Cast to a signed integer of the same size. |
43 | /// |
44 | /// This trait is implemented for all integers. Unsigned to signed casts are equivalent to |
45 | /// `0.wrapping_add_signed(value)`, while signed to signed casts are an identity conversion. |
46 | /// |
47 | /// ```rust |
48 | /// # use num_conv::CastSigned; |
49 | /// assert_eq!(u8::MAX.cast_signed(), -1_i8); |
50 | /// assert_eq!(u16::MAX.cast_signed(), -1_i16); |
51 | /// assert_eq!(u32::MAX.cast_signed(), -1_i32); |
52 | /// assert_eq!(u64::MAX.cast_signed(), -1_i64); |
53 | /// assert_eq!(u128::MAX.cast_signed(), -1_i128); |
54 | /// assert_eq!(usize::MAX.cast_signed(), -1_isize); |
55 | /// ``` |
56 | /// |
57 | /// ```rust |
58 | /// # use num_conv::CastSigned; |
59 | /// assert_eq!(0_i8.cast_signed(), 0_i8); |
60 | /// assert_eq!(0_i16.cast_signed(), 0_i16); |
61 | /// assert_eq!(0_i32.cast_signed(), 0_i32); |
62 | /// assert_eq!(0_i64.cast_signed(), 0_i64); |
63 | /// assert_eq!(0_i128.cast_signed(), 0_i128); |
64 | /// assert_eq!(0_isize.cast_signed(), 0_isize); |
65 | /// ``` |
66 | pub trait CastSigned: sealed::Integer { |
67 | /// The signed integer type with the same size as `Self`. |
68 | type Signed; |
69 | |
70 | /// Cast an integer to the signed integer of the same size. |
71 | fn cast_signed(self) -> Self::Signed; |
72 | } |
73 | |
74 | /// Cast to an unsigned integer of the same size. |
75 | /// |
76 | /// This trait is implemented for all integers. Signed to unsigned casts are equivalent to |
77 | /// `0.wrapping_add_unsigned(value)`, while unsigned to unsigned casts are an identity conversion. |
78 | /// |
79 | /// ```rust |
80 | /// # use num_conv::CastUnsigned; |
81 | /// assert_eq!((-1_i8).cast_unsigned(), u8::MAX); |
82 | /// assert_eq!((-1_i16).cast_unsigned(), u16::MAX); |
83 | /// assert_eq!((-1_i32).cast_unsigned(), u32::MAX); |
84 | /// assert_eq!((-1_i64).cast_unsigned(), u64::MAX); |
85 | /// assert_eq!((-1_i128).cast_unsigned(), u128::MAX); |
86 | /// assert_eq!((-1_isize).cast_unsigned(), usize::MAX); |
87 | /// ``` |
88 | /// |
89 | /// ```rust |
90 | /// # use num_conv::CastUnsigned; |
91 | /// assert_eq!(0_u8.cast_unsigned(), 0_u8); |
92 | /// assert_eq!(0_u16.cast_unsigned(), 0_u16); |
93 | /// assert_eq!(0_u32.cast_unsigned(), 0_u32); |
94 | /// assert_eq!(0_u64.cast_unsigned(), 0_u64); |
95 | /// assert_eq!(0_u128.cast_unsigned(), 0_u128); |
96 | /// assert_eq!(0_usize.cast_unsigned(), 0_usize); |
97 | /// ``` |
98 | pub trait CastUnsigned: sealed::Integer { |
99 | /// The unsigned integer type with the same size as `Self`. |
100 | type Unsigned; |
101 | |
102 | /// Cast an integer to the unsigned integer of the same size. |
103 | fn cast_unsigned(self) -> Self::Unsigned; |
104 | } |
105 | |
106 | /// A type that can be used with turbofish syntax in [`Extend::extend`]. |
107 | /// |
108 | /// It is unlikely that you will want to use this trait directly. You are probably looking for the |
109 | /// [`Extend`] trait. |
110 | pub trait ExtendTarget<T>: sealed::ExtendTargetSealed<T> {} |
111 | |
112 | /// A type that can be used with turbofish syntax in [`Truncate::truncate`]. |
113 | /// |
114 | /// It is unlikely that you will want to use this trait directly. You are probably looking for the |
115 | /// [`Truncate`] trait. |
116 | pub trait TruncateTarget<T>: sealed::TruncateTargetSealed<T> {} |
117 | |
118 | /// Extend to an integer of the same size or larger, preserving its value. |
119 | /// |
120 | /// ```rust |
121 | /// # use num_conv::Extend; |
122 | /// assert_eq!(0_u8.extend::<u16>(), 0_u16); |
123 | /// assert_eq!(0_u16.extend::<u32>(), 0_u32); |
124 | /// assert_eq!(0_u32.extend::<u64>(), 0_u64); |
125 | /// assert_eq!(0_u64.extend::<u128>(), 0_u128); |
126 | /// ``` |
127 | /// |
128 | /// ```rust |
129 | /// # use num_conv::Extend; |
130 | /// assert_eq!((-1_i8).extend::<i16>(), -1_i16); |
131 | /// assert_eq!((-1_i16).extend::<i32>(), -1_i32); |
132 | /// assert_eq!((-1_i32).extend::<i64>(), -1_i64); |
133 | /// assert_eq!((-1_i64).extend::<i128>(), -1_i128); |
134 | /// ``` |
135 | pub trait Extend: sealed::Integer { |
136 | /// Extend an integer to an integer of the same size or larger, preserving its value. |
137 | fn extend<T>(self) -> T |
138 | where |
139 | Self: ExtendTarget<T>; |
140 | } |
141 | |
142 | impl<T: sealed::Integer> Extend for T { |
143 | fn extend<U>(self) -> U |
144 | where |
145 | T: ExtendTarget<U>, |
146 | { |
147 | sealed::ExtendTargetSealed::extend(self) |
148 | } |
149 | } |
150 | |
151 | /// Truncate to an integer of the same size or smaller, preserving the least significant bits. |
152 | /// |
153 | /// ```rust |
154 | /// # use num_conv::Truncate; |
155 | /// assert_eq!(u16::MAX.truncate::<u8>(), u8::MAX); |
156 | /// assert_eq!(u32::MAX.truncate::<u16>(), u16::MAX); |
157 | /// assert_eq!(u64::MAX.truncate::<u32>(), u32::MAX); |
158 | /// assert_eq!(u128::MAX.truncate::<u64>(), u64::MAX); |
159 | /// ``` |
160 | /// |
161 | /// ```rust |
162 | /// # use num_conv::Truncate; |
163 | /// assert_eq!((-1_i16).truncate::<i8>(), -1_i8); |
164 | /// assert_eq!((-1_i32).truncate::<i16>(), -1_i16); |
165 | /// assert_eq!((-1_i64).truncate::<i32>(), -1_i32); |
166 | /// assert_eq!((-1_i128).truncate::<i64>(), -1_i64); |
167 | /// ``` |
168 | pub trait Truncate: sealed::Integer { |
169 | /// Truncate an integer to an integer of the same size or smaller, preserving the least |
170 | /// significant bits. |
171 | fn truncate<T>(self) -> T |
172 | where |
173 | Self: TruncateTarget<T>; |
174 | } |
175 | |
176 | impl<T: sealed::Integer> Truncate for T { |
177 | fn truncate<U>(self) -> U |
178 | where |
179 | T: TruncateTarget<U>, |
180 | { |
181 | sealed::TruncateTargetSealed::truncate(self) |
182 | } |
183 | } |
184 | |
185 | macro_rules! impl_cast_signed { |
186 | ($($($from:ty),+ => $to:ty;)*) => {$($( |
187 | const _: () = assert!( |
188 | core::mem::size_of::<$from>() == core::mem::size_of::<$to>(), |
189 | concat!( |
190 | "cannot cast " , |
191 | stringify!($from), |
192 | " to " , |
193 | stringify!($to), |
194 | " because they are different sizes" |
195 | ) |
196 | ); |
197 | |
198 | impl CastSigned for $from { |
199 | type Signed = $to; |
200 | fn cast_signed(self) -> Self::Signed { |
201 | self as _ |
202 | } |
203 | } |
204 | )+)*}; |
205 | } |
206 | |
207 | macro_rules! impl_cast_unsigned { |
208 | ($($($from:ty),+ => $to:ty;)*) => {$($( |
209 | const _: () = assert!( |
210 | core::mem::size_of::<$from>() == core::mem::size_of::<$to>(), |
211 | concat!( |
212 | "cannot cast " , |
213 | stringify!($from), |
214 | " to " , |
215 | stringify!($to), |
216 | " because they are different sizes" |
217 | ) |
218 | ); |
219 | |
220 | impl CastUnsigned for $from { |
221 | type Unsigned = $to; |
222 | fn cast_unsigned(self) -> Self::Unsigned { |
223 | self as _ |
224 | } |
225 | } |
226 | )+)*}; |
227 | } |
228 | |
229 | macro_rules! impl_extend { |
230 | ($($from:ty => $($to:ty),+;)*) => {$($( |
231 | const _: () = assert!( |
232 | core::mem::size_of::<$from>() <= core::mem::size_of::<$to>(), |
233 | concat!( |
234 | "cannot extend " , |
235 | stringify!($from), |
236 | " to " , |
237 | stringify!($to), |
238 | " because " , |
239 | stringify!($from), |
240 | " is larger than " , |
241 | stringify!($to) |
242 | ) |
243 | ); |
244 | |
245 | impl sealed::ExtendTargetSealed<$to> for $from { |
246 | fn extend(self) -> $to { |
247 | self as _ |
248 | } |
249 | } |
250 | |
251 | impl ExtendTarget<$to> for $from {} |
252 | )+)*}; |
253 | } |
254 | |
255 | macro_rules! impl_truncate { |
256 | ($($($from:ty),+ => $to:ty;)*) => {$($( |
257 | const _: () = assert!( |
258 | core::mem::size_of::<$from>() >= core::mem::size_of::<$to>(), |
259 | concat!( |
260 | "cannot truncate " , |
261 | stringify!($from), |
262 | " to " , |
263 | stringify!($to), |
264 | " because " , |
265 | stringify!($from), |
266 | " is smaller than " , |
267 | stringify!($to) |
268 | ) |
269 | ); |
270 | |
271 | impl sealed::TruncateTargetSealed<$to> for $from { |
272 | fn truncate(self) -> $to { |
273 | self as _ |
274 | } |
275 | } |
276 | |
277 | impl TruncateTarget<$to> for $from {} |
278 | )+)*}; |
279 | } |
280 | |
281 | impl_cast_signed! { |
282 | u8, i8 => i8; |
283 | u16, i16 => i16; |
284 | u32, i32 => i32; |
285 | u64, i64 => i64; |
286 | u128, i128 => i128; |
287 | usize, isize => isize; |
288 | } |
289 | |
290 | impl_cast_unsigned! { |
291 | u8, i8 => u8; |
292 | u16, i16 => u16; |
293 | u32, i32 => u32; |
294 | u64, i64 => u64; |
295 | u128, i128 => u128; |
296 | usize, isize => usize; |
297 | } |
298 | |
299 | impl_extend! { |
300 | u8 => u8, u16, u32, u64, u128, usize; |
301 | u16 => u16, u32, u64, u128, usize; |
302 | u32 => u32, u64, u128; |
303 | u64 => u64, u128; |
304 | u128 => u128; |
305 | usize => usize; |
306 | |
307 | i8 => i8, i16, i32, i64, i128, isize; |
308 | i16 => i16, i32, i64, i128, isize; |
309 | i32 => i32, i64, i128; |
310 | i64 => i64, i128; |
311 | i128 => i128; |
312 | isize => isize; |
313 | } |
314 | |
315 | impl_truncate! { |
316 | u8, u16, u32, u64, u128, usize => u8; |
317 | u16, u32, u64, u128, usize => u16; |
318 | u32, u64, u128 => u32; |
319 | u64, u128 => u64; |
320 | u128 => u128; |
321 | usize => usize; |
322 | |
323 | i8, i16, i32, i64, i128, isize => i8; |
324 | i16, i32, i64, i128, isize => i16; |
325 | i32, i64, i128 => i32; |
326 | i64, i128 => i64; |
327 | i128 => i128; |
328 | isize => isize; |
329 | } |
330 | |