11use crate :: PrimitiveError ;
22
33trait Sealed { }
4+ struct SealedToken ;
45
56/// Trait for all primitive [numeric types].
67///
@@ -45,6 +46,20 @@ trait Sealed {}
4546pub trait PrimitiveNumber :
4647 ' static
4748 + 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 >
4863 + core:: cmp:: PartialEq
4964 + core:: cmp:: PartialOrd
5065 + core:: convert:: From < bool >
@@ -89,13 +104,13 @@ pub trait PrimitiveNumber:
89104 /// [`to_be_bytes`][Self::to_be_bytes]. It is effectively `[u8; size_of::<Self>()]`.
90105 type Bytes : core:: borrow:: Borrow < [ u8 ] > + core:: borrow:: BorrowMut < [ u8 ] > ;
91106
92- /// Creates a value from its representation as a byte array in big endian.
107+ /// Creates a number from its representation as a byte array in big endian.
93108 fn from_be_bytes ( bytes : Self :: Bytes ) -> Self ;
94109
95- /// Creates a value from its representation as a byte array in little endian.
110+ /// Creates a number from its representation as a byte array in little endian.
96111 fn from_le_bytes ( bytes : Self :: Bytes ) -> Self ;
97112
98- /// Creates a value from its representation as a byte array in native endian.
113+ /// Creates a number from its representation as a byte array in native endian.
99114 fn from_ne_bytes ( bytes : Self :: Bytes ) -> Self ;
100115
101116 /// Returns the memory representation of this number as a byte array in little-endian order.
@@ -106,6 +121,28 @@ pub trait PrimitiveNumber:
106121
107122 /// Returns the memory representation of this number as a byte array in native-endian order.
108123 fn to_ne_bytes ( self ) -> Self :: Bytes ;
124+
125+ /// Creates a number using a type cast, `value as Self`.
126+ ///
127+ /// Note: unlike other `num-primitive` methods, there is no inherent method by this name on the
128+ /// actual types.
129+ fn as_from < T > ( value : T ) -> Self
130+ where
131+ Self : PrimitiveNumberAs < T > ,
132+ {
133+ <Self as PrimitiveNumberAs < T > >:: __as_from ( value, SealedToken )
134+ }
135+
136+ /// Converts this number with a type cast, `self as T`.
137+ ///
138+ /// Note: unlike other `num-primitive` methods, there is no inherent method by this name on the
139+ /// actual types.
140+ fn as_to < T > ( self ) -> T
141+ where
142+ Self : PrimitiveNumberAs < T > ,
143+ {
144+ <Self as PrimitiveNumberAs < T > >:: __as_to ( self , SealedToken )
145+ }
109146}
110147
111148/// Trait for references to primitive numbers ([`PrimitiveNumber`]).
@@ -142,8 +179,71 @@ pub trait PrimitiveNumberRef<T>:
142179{
143180}
144181
182+ /// Trait for numeric conversions supported by the [`as`] keyword.
183+ ///
184+ /// This is effectively the same as the [type cast] expression `self as N`, implemented for all
185+ /// combinations of [`PrimitiveNumber`].
186+ ///
187+ /// [`as`]: https://doc.rust-lang.org/std/keyword.as.html
188+ /// [type cast]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions
189+ ///
190+ /// # Examples
191+ ///
192+ /// `PrimitiveNumberAs<{number}>` is a supertrait of [`PrimitiveNumber`] for all primitive floats
193+ /// and integers, so you do not need to use this trait directly when converting concrete types.
194+ ///
195+ /// ```
196+ /// use num_primitive::PrimitiveNumber;
197+ ///
198+ /// // Clamp any number to the interval 0..=100, unless it is NaN.
199+ /// fn clamp_percentage<Number: PrimitiveNumber>(x: Number) -> Number {
200+ /// let clamped = x.as_to::<f64>().clamp(0.0, 100.0);
201+ /// Number::as_from(clamped)
202+ /// }
203+ ///
204+ /// assert_eq!(clamp_percentage(-42_i8), 0_i8);
205+ /// assert_eq!(clamp_percentage(42_u128), 42_u128);
206+ /// assert_eq!(clamp_percentage(1e100_f64), 100_f64);
207+ /// assert!(clamp_percentage(f32::NAN).is_nan());
208+ /// ```
209+ ///
210+ /// However, if the other type is also generic, an explicit type constraint is needed.
211+ ///
212+ /// ```
213+ /// use num_primitive::{PrimitiveNumber, PrimitiveNumberAs};
214+ ///
215+ /// fn clamp_any<Number, Limit>(x: Number, min: Limit, max: Limit) -> Number
216+ /// where
217+ /// Number: PrimitiveNumber + PrimitiveNumberAs<Limit>,
218+ /// Limit: PartialOrd,
219+ /// {
220+ /// assert!(min <= max);
221+ /// let y = x.as_to::<Limit>();
222+ /// if y <= min {
223+ /// Number::as_from(min)
224+ /// } else if y >= max {
225+ /// Number::as_from(max)
226+ /// } else {
227+ /// x
228+ /// }
229+ /// }
230+ ///
231+ /// assert_eq!(clamp_any(1.23, 0_i8, 10_i8), 1.23);
232+ /// assert_eq!(clamp_any(1.23, -1_i8, 1_i8), 1.0);
233+ /// assert_eq!(clamp_any(i128::MAX, 0.0, 1e100), i128::MAX);
234+ /// ```
235+ pub trait PrimitiveNumberAs < T > {
236+ #[ doc( hidden) ]
237+ #[ expect( private_interfaces) ]
238+ fn __as_from ( x : T , _: SealedToken ) -> Self ;
239+
240+ #[ doc( hidden) ]
241+ #[ expect( private_interfaces) ]
242+ fn __as_to ( x : Self , _: SealedToken ) -> T ;
243+ }
244+
145245macro_rules! impl_primitive {
146- ( $( $Number: ident) ,* ) => { $(
246+ ( $( $Number: ident) ,+ ) => { $(
147247 impl Sealed for $Number { }
148248 impl Sealed for & $Number { }
149249
@@ -163,7 +263,27 @@ macro_rules! impl_primitive {
163263 }
164264
165265 impl PrimitiveNumberRef <$Number> for & $Number { }
166- ) * }
266+
267+ impl_primitive!( $Number as f32 , f64 ) ;
268+ impl_primitive!( $Number as i8 , i16 , i32 , i64 , i128 , isize ) ;
269+ impl_primitive!( $Number as u8 , u16 , u32 , u64 , u128 , usize ) ;
270+ ) +} ;
271+
272+ ( $Number: ident as $( $Other: ident) ,+) => { $(
273+ impl PrimitiveNumberAs <$Other> for $Number {
274+ #[ inline]
275+ #[ expect( private_interfaces) ]
276+ fn __as_from( x: $Other, _: SealedToken ) -> Self {
277+ x as Self
278+ }
279+
280+ #[ inline]
281+ #[ expect( private_interfaces) ]
282+ fn __as_to( x: Self , _: SealedToken ) -> $Other {
283+ x as $Other
284+ }
285+ }
286+ ) +}
167287}
168288
169289impl_primitive ! ( f32 , f64 ) ;
0 commit comments