num_primitive/number.rs
1use crate::PrimitiveError;
2
3trait Sealed {}
4struct SealedToken;
5
6/// Trait for all primitive [numeric types].
7///
8/// This encapsulates trait implementations and inherent methods that are common among all of the
9/// primitive numeric types: [`f32`], [`f64`], [`i8`], [`i16`], [`i32`], [`i64`], [`i128`],
10/// [`isize`], [`u8`], [`u16`], [`u32`], [`u64`], [`u128`], and [`usize`]. Unstable types like
11/// [`f16`] and [`f128`] will be added once they are stabilized.
12///
13/// See the corresponding items on the individual types for more documentation and examples.
14///
15/// This trait is sealed with a private trait to prevent downstream implementations, so we may
16/// continue to expand along with the standard library without worrying about breaking changes for
17/// implementors.
18///
19/// [numeric types]: https://doc.rust-lang.org/reference/types/numeric.html
20///
21/// # Examples
22///
23/// ```
24/// use num_primitive::{PrimitiveNumber, PrimitiveNumberRef};
25///
26/// fn dot_product<T: PrimitiveNumber>(a: &[T], b: &[T]) -> T {
27/// assert_eq!(a.len(), b.len());
28/// // Note that we have to dereference to use `T: Mul<&T>`
29/// core::iter::zip(a, b).map(|(a, b)| (*a) * b).sum()
30/// }
31///
32/// fn dot_product_ref<T>(a: &[T], b: &[T]) -> T
33/// where
34/// T: PrimitiveNumber,
35/// for<'a> &'a T: PrimitiveNumberRef<T>,
36/// {
37/// assert_eq!(a.len(), b.len());
38/// // In this case we can use `&T: Mul`
39/// core::iter::zip(a, b).map(|(a, b)| a * b).sum()
40/// }
41///
42/// assert_eq!(dot_product::<i32>(&[1, 3, -5], &[4, -2, -1]), 3);
43/// assert_eq!(dot_product_ref::<f64>(&[1., 3., -5.], &[4., -2., -1.]), 3.);
44/// ```
45#[expect(private_bounds)]
46pub trait PrimitiveNumber:
47 'static
48 + Sealed
49 + PrimitiveNumberAs<f32>
50 + PrimitiveNumberAs<f64>
51 + PrimitiveNumberAs<i8>
52 + PrimitiveNumberAs<i16>
53 + PrimitiveNumberAs<i32>
54 + PrimitiveNumberAs<i64>
55 + PrimitiveNumberAs<i128>
56 + PrimitiveNumberAs<isize>
57 + PrimitiveNumberAs<u8>
58 + PrimitiveNumberAs<u16>
59 + PrimitiveNumberAs<u32>
60 + PrimitiveNumberAs<u64>
61 + PrimitiveNumberAs<u128>
62 + PrimitiveNumberAs<usize>
63 + core::cmp::PartialEq
64 + core::cmp::PartialOrd
65 + core::convert::From<bool>
66 + core::default::Default
67 + core::fmt::Debug
68 + core::fmt::Display
69 + core::fmt::LowerExp
70 + core::fmt::UpperExp
71 + core::iter::Product<Self>
72 + core::iter::Sum<Self>
73 + core::marker::Copy
74 + core::marker::Send
75 + core::marker::Sync
76 + core::marker::Unpin
77 + core::ops::Add<Self, Output = Self>
78 + core::ops::AddAssign<Self>
79 + core::ops::Div<Self, Output = Self>
80 + core::ops::DivAssign<Self>
81 + core::ops::Mul<Self, Output = Self>
82 + core::ops::MulAssign<Self>
83 + core::ops::Rem<Self, Output = Self>
84 + core::ops::RemAssign<Self>
85 + core::ops::Sub<Self, Output = Self>
86 + core::ops::SubAssign<Self>
87 + core::panic::RefUnwindSafe
88 + core::panic::UnwindSafe
89 + core::str::FromStr<Err: PrimitiveError>
90 + for<'a> core::iter::Product<&'a Self>
91 + for<'a> core::iter::Sum<&'a Self>
92 + for<'a> core::ops::Add<&'a Self, Output = Self>
93 + for<'a> core::ops::AddAssign<&'a Self>
94 + for<'a> core::ops::Div<&'a Self, Output = Self>
95 + for<'a> core::ops::DivAssign<&'a Self>
96 + for<'a> core::ops::Mul<&'a Self, Output = Self>
97 + for<'a> core::ops::MulAssign<&'a Self>
98 + for<'a> core::ops::Rem<&'a Self, Output = Self>
99 + for<'a> core::ops::RemAssign<&'a Self>
100 + for<'a> core::ops::Sub<&'a Self, Output = Self>
101 + for<'a> core::ops::SubAssign<&'a Self>
102{
103 /// An array of bytes used by methods like [`from_be_bytes`][Self::from_be_bytes] and
104 /// [`to_be_bytes`][Self::to_be_bytes]. It is effectively `[u8; size_of::<Self>()]`.
105 type Bytes: 'static
106 + core::borrow::Borrow<[u8]>
107 + core::borrow::BorrowMut<[u8]>
108 + core::cmp::Eq
109 + core::cmp::Ord
110 + core::cmp::PartialEq<[u8]>
111 + core::convert::AsRef<[u8]>
112 + core::convert::AsMut<[u8]>
113 + core::default::Default
114 + core::fmt::Debug
115 + core::hash::Hash
116 + core::marker::Copy
117 + core::marker::Send
118 + core::marker::Sync
119 + core::marker::Unpin
120 + core::panic::RefUnwindSafe
121 + core::panic::UnwindSafe;
122
123 /// Creates a number from its representation as a byte array in big endian.
124 fn from_be_bytes(bytes: Self::Bytes) -> Self;
125
126 /// Creates a number from its representation as a byte array in little endian.
127 fn from_le_bytes(bytes: Self::Bytes) -> Self;
128
129 /// Creates a number from its representation as a byte array in native endian.
130 fn from_ne_bytes(bytes: Self::Bytes) -> Self;
131
132 /// Returns the memory representation of this number as a byte array in little-endian order.
133 fn to_be_bytes(self) -> Self::Bytes;
134
135 /// Returns the memory representation of this number as a byte array in big-endian order.
136 fn to_le_bytes(self) -> Self::Bytes;
137
138 /// Returns the memory representation of this number as a byte array in native-endian order.
139 fn to_ne_bytes(self) -> Self::Bytes;
140
141 /// Creates a number using a type cast, `value as Self`.
142 ///
143 /// Note: unlike other `num-primitive` methods, there is no inherent method by this name on the
144 /// actual types.
145 fn as_from<T>(value: T) -> Self
146 where
147 Self: PrimitiveNumberAs<T>,
148 {
149 <Self as PrimitiveNumberAs<T>>::__as_from(value, SealedToken)
150 }
151
152 /// Converts this number with a type cast, `self as T`.
153 ///
154 /// Note: unlike other `num-primitive` methods, there is no inherent method by this name on the
155 /// actual types.
156 fn as_to<T>(self) -> T
157 where
158 Self: PrimitiveNumberAs<T>,
159 {
160 <Self as PrimitiveNumberAs<T>>::__as_to(self, SealedToken)
161 }
162}
163
164/// Trait for references to primitive numbers ([`PrimitiveNumber`]).
165///
166/// This enables traits like the standard operators in generic code,
167/// e.g. `where &T: PrimitiveNumberRef<T>`.
168#[expect(private_bounds)]
169pub trait PrimitiveNumberRef<T>:
170 Sealed
171 + core::borrow::Borrow<T>
172 + core::cmp::PartialEq
173 + core::cmp::PartialOrd
174 + core::fmt::Debug
175 + core::fmt::Display
176 + core::fmt::LowerExp
177 + core::fmt::UpperExp
178 + core::marker::Copy
179 + core::marker::Send
180 + core::marker::Sync
181 + core::marker::Unpin
182 + core::ops::Add<T, Output = T>
183 + core::ops::Deref<Target = T>
184 + core::ops::Div<T, Output = T>
185 + core::ops::Mul<T, Output = T>
186 + core::ops::Rem<T, Output = T>
187 + core::ops::Sub<T, Output = T>
188 + core::panic::RefUnwindSafe
189 + core::panic::UnwindSafe
190 + for<'a> core::ops::Add<&'a T, Output = T>
191 + for<'a> core::ops::Div<&'a T, Output = T>
192 + for<'a> core::ops::Mul<&'a T, Output = T>
193 + for<'a> core::ops::Rem<&'a T, Output = T>
194 + for<'a> core::ops::Sub<&'a T, Output = T>
195{
196}
197
198/// Trait for numeric conversions supported by the [`as`] keyword.
199///
200/// This is effectively the same as the [type cast] expression `self as N`, implemented for all
201/// combinations of [`PrimitiveNumber`].
202///
203/// [`as`]: https://doc.rust-lang.org/std/keyword.as.html
204/// [type cast]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions
205///
206/// # Examples
207///
208/// `PrimitiveNumberAs<{number}>` is a supertrait of [`PrimitiveNumber`] for all primitive floats
209/// and integers, so you do not need to use this trait directly when converting concrete types.
210///
211/// ```
212/// use num_primitive::PrimitiveNumber;
213///
214/// // Clamp any number to the interval 0..=100, unless it is NaN.
215/// fn clamp_percentage<Number: PrimitiveNumber>(x: Number) -> Number {
216/// let clamped = x.as_to::<f64>().clamp(0.0, 100.0);
217/// Number::as_from(clamped)
218/// }
219///
220/// assert_eq!(clamp_percentage(-42_i8), 0_i8);
221/// assert_eq!(clamp_percentage(42_u128), 42_u128);
222/// assert_eq!(clamp_percentage(1e100_f64), 100_f64);
223/// assert!(clamp_percentage(f32::NAN).is_nan());
224/// ```
225///
226/// However, if the other type is also generic, an explicit type constraint is needed.
227///
228/// ```
229/// use num_primitive::{PrimitiveNumber, PrimitiveNumberAs};
230///
231/// fn clamp_any<Number, Limit>(x: Number, min: Limit, max: Limit) -> Number
232/// where
233/// Number: PrimitiveNumber + PrimitiveNumberAs<Limit>,
234/// Limit: PartialOrd,
235/// {
236/// assert!(min <= max);
237/// let y = x.as_to::<Limit>();
238/// if y <= min {
239/// Number::as_from(min)
240/// } else if y >= max {
241/// Number::as_from(max)
242/// } else {
243/// x
244/// }
245/// }
246///
247/// assert_eq!(clamp_any(1.23, 0_i8, 10_i8), 1.23);
248/// assert_eq!(clamp_any(1.23, -1_i8, 1_i8), 1.0);
249/// assert_eq!(clamp_any(i128::MAX, 0.0, 1e100), i128::MAX);
250/// ```
251pub trait PrimitiveNumberAs<T> {
252 #[doc(hidden)]
253 #[expect(private_interfaces)]
254 fn __as_from(x: T, _: SealedToken) -> Self;
255
256 #[doc(hidden)]
257 #[expect(private_interfaces)]
258 fn __as_to(x: Self, _: SealedToken) -> T;
259}
260
261macro_rules! impl_primitive {
262 ($($Number:ident),+) => {$(
263 impl Sealed for $Number {}
264 impl Sealed for &$Number {}
265
266 impl PrimitiveNumber for $Number {
267 type Bytes = [u8; size_of::<Self>()];
268
269 forward! {
270 fn from_be_bytes(bytes: Self::Bytes) -> Self;
271 fn from_le_bytes(bytes: Self::Bytes) -> Self;
272 fn from_ne_bytes(bytes: Self::Bytes) -> Self;
273 }
274 forward! {
275 fn to_be_bytes(self) -> Self::Bytes;
276 fn to_le_bytes(self) -> Self::Bytes;
277 fn to_ne_bytes(self) -> Self::Bytes;
278 }
279 }
280
281 impl PrimitiveNumberRef<$Number> for &$Number {}
282
283 impl_primitive!($Number as f32, f64);
284 impl_primitive!($Number as i8, i16, i32, i64, i128, isize);
285 impl_primitive!($Number as u8, u16, u32, u64, u128, usize);
286 )+};
287
288 ($Number:ident as $($Other:ident),+) => {$(
289 impl PrimitiveNumberAs<$Other> for $Number {
290 #[inline]
291 #[expect(private_interfaces)]
292 fn __as_from(x: $Other, _: SealedToken) -> Self {
293 x as Self
294 }
295
296 #[inline]
297 #[expect(private_interfaces)]
298 fn __as_to(x: Self, _: SealedToken) -> $Other {
299 x as $Other
300 }
301 }
302 )+}
303}
304
305impl_primitive!(f32, f64);
306impl_primitive!(i8, i16, i32, i64, i128, isize);
307impl_primitive!(u8, u16, u32, u64, u128, usize);