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    /// Calculates the midpoint (average) between `self` and `other`.
133    fn midpoint(self, other: Self) -> Self;
134
135    /// Returns the memory representation of this number as a byte array in little-endian order.
136    fn to_be_bytes(self) -> Self::Bytes;
137
138    /// Returns the memory representation of this number as a byte array in big-endian order.
139    fn to_le_bytes(self) -> Self::Bytes;
140
141    /// Returns the memory representation of this number as a byte array in native-endian order.
142    fn to_ne_bytes(self) -> Self::Bytes;
143
144    /// Creates a number using a type cast, `value as Self`.
145    ///
146    /// Note: unlike other `num-primitive` methods, there is no inherent method by this name on the
147    /// actual types.
148    fn as_from<T>(value: T) -> Self
149    where
150        Self: PrimitiveNumberAs<T>,
151    {
152        <Self as PrimitiveNumberAs<T>>::__as_from(value, SealedToken)
153    }
154
155    /// Converts this number with a type cast, `self as T`.
156    ///
157    /// Note: unlike other `num-primitive` methods, there is no inherent method by this name on the
158    /// actual types.
159    fn as_to<T>(self) -> T
160    where
161        Self: PrimitiveNumberAs<T>,
162    {
163        <Self as PrimitiveNumberAs<T>>::__as_to(self, SealedToken)
164    }
165}
166
167/// Trait for references to primitive numbers ([`PrimitiveNumber`]).
168///
169/// This enables traits like the standard operators in generic code,
170/// e.g. `where &T: PrimitiveNumberRef<T>`.
171#[expect(private_bounds)]
172pub trait PrimitiveNumberRef<T>:
173    Sealed
174    + core::borrow::Borrow<T>
175    + core::cmp::PartialEq
176    + core::cmp::PartialOrd
177    + core::fmt::Debug
178    + core::fmt::Display
179    + core::fmt::LowerExp
180    + core::fmt::UpperExp
181    + core::marker::Copy
182    + core::marker::Send
183    + core::marker::Sync
184    + core::marker::Unpin
185    + core::ops::Add<T, Output = T>
186    + core::ops::Deref<Target = T>
187    + core::ops::Div<T, Output = T>
188    + core::ops::Mul<T, Output = T>
189    + core::ops::Rem<T, Output = T>
190    + core::ops::Sub<T, Output = T>
191    + core::panic::RefUnwindSafe
192    + core::panic::UnwindSafe
193    + for<'a> core::ops::Add<&'a T, Output = T>
194    + for<'a> core::ops::Div<&'a T, Output = T>
195    + for<'a> core::ops::Mul<&'a T, Output = T>
196    + for<'a> core::ops::Rem<&'a T, Output = T>
197    + for<'a> core::ops::Sub<&'a T, Output = T>
198{
199}
200
201/// Trait for numeric conversions supported by the [`as`] keyword.
202///
203/// This is effectively the same as the [type cast] expression `self as N`, implemented for all
204/// combinations of [`PrimitiveNumber`].
205///
206/// [`as`]: https://doc.rust-lang.org/std/keyword.as.html
207/// [type cast]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions
208///
209/// # Examples
210///
211/// `PrimitiveNumberAs<{number}>` is a supertrait of [`PrimitiveNumber`] for all primitive floats
212/// and integers, so you do not need to use this trait directly when converting concrete types.
213///
214/// ```
215/// use num_primitive::PrimitiveNumber;
216///
217/// // Clamp any number to the interval 0..=100, unless it is NaN.
218/// fn clamp_percentage<Number: PrimitiveNumber>(x: Number) -> Number {
219///     let clamped = x.as_to::<f64>().clamp(0.0, 100.0);
220///     Number::as_from(clamped)
221/// }
222///
223/// assert_eq!(clamp_percentage(-42_i8), 0_i8);
224/// assert_eq!(clamp_percentage(42_u128), 42_u128);
225/// assert_eq!(clamp_percentage(1e100_f64), 100_f64);
226/// assert!(clamp_percentage(f32::NAN).is_nan());
227/// ```
228///
229/// However, if the other type is also generic, an explicit type constraint is needed.
230///
231/// ```
232/// use num_primitive::{PrimitiveNumber, PrimitiveNumberAs};
233///
234/// fn clamp_any<Number, Limit>(x: Number, min: Limit, max: Limit) -> Number
235/// where
236///     Number: PrimitiveNumber + PrimitiveNumberAs<Limit>,
237///     Limit: PartialOrd,
238/// {
239///     assert!(min <= max);
240///     let y = x.as_to::<Limit>();
241///     if y <= min {
242///         Number::as_from(min)
243///     } else if y >= max {
244///         Number::as_from(max)
245///     } else {
246///         x
247///     }
248/// }
249///
250/// assert_eq!(clamp_any(1.23, 0_i8, 10_i8), 1.23);
251/// assert_eq!(clamp_any(1.23, -1_i8, 1_i8), 1.0);
252/// assert_eq!(clamp_any(i128::MAX, 0.0, 1e100), i128::MAX);
253/// ```
254pub trait PrimitiveNumberAs<T> {
255    #[doc(hidden)]
256    #[expect(private_interfaces)]
257    fn __as_from(x: T, _: SealedToken) -> Self;
258
259    #[doc(hidden)]
260    #[expect(private_interfaces)]
261    fn __as_to(x: Self, _: SealedToken) -> T;
262}
263
264macro_rules! impl_primitive {
265    ($($Number:ident),+) => {$(
266        impl Sealed for $Number {}
267        impl Sealed for &$Number {}
268
269        impl PrimitiveNumber for $Number {
270            type Bytes = [u8; size_of::<Self>()];
271
272            forward! {
273                fn from_be_bytes(bytes: Self::Bytes) -> Self;
274                fn from_le_bytes(bytes: Self::Bytes) -> Self;
275                fn from_ne_bytes(bytes: Self::Bytes) -> Self;
276            }
277            forward! {
278                fn midpoint(self, other: Self) -> Self;
279                fn to_be_bytes(self) -> Self::Bytes;
280                fn to_le_bytes(self) -> Self::Bytes;
281                fn to_ne_bytes(self) -> Self::Bytes;
282            }
283        }
284
285        impl PrimitiveNumberRef<$Number> for &$Number {}
286
287        impl_primitive!($Number as f32, f64);
288        impl_primitive!($Number as i8, i16, i32, i64, i128, isize);
289        impl_primitive!($Number as u8, u16, u32, u64, u128, usize);
290    )+};
291
292    ($Number:ident as $($Other:ident),+) => {$(
293        impl PrimitiveNumberAs<$Other> for $Number {
294            #[inline]
295            #[expect(private_interfaces)]
296            fn __as_from(x: $Other, _: SealedToken) -> Self {
297                x as Self
298            }
299
300            #[inline]
301            #[expect(private_interfaces)]
302            fn __as_to(x: Self, _: SealedToken) -> $Other {
303                x as $Other
304            }
305        }
306    )+}
307}
308
309impl_primitive!(f32, f64);
310impl_primitive!(i8, i16, i32, i64, i128, isize);
311impl_primitive!(u8, u16, u32, u64, u128, usize);