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}