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! {
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
324impl_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
340impl_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}