Skip to content

Commit 397037d

Browse files
committed
Implement NumCast for NonZero*
1 parent 2b9af3d commit 397037d

File tree

2 files changed

+259
-0
lines changed

2 files changed

+259
-0
lines changed

src/cast.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,34 @@ impl_num_cast!(isize, to_isize);
754754
impl_num_cast!(f32, to_f32);
755755
impl_num_cast!(f64, to_f64);
756756

757+
macro_rules! impl_num_cast_nonzero {
758+
($T:ty, $conv:ident) => {
759+
impl NumCast for $T {
760+
#[inline]
761+
#[allow(deprecated)]
762+
fn from<N: ToPrimitive>(n: N) -> Option<$T> {
763+
// `$conv` could be generated using `concat_idents!`, but that
764+
// macro seems to be broken at the moment
765+
n.$conv().and_then(Self::new)
766+
}
767+
}
768+
};
769+
}
770+
771+
impl_num_cast_nonzero!(NonZeroUsize, to_usize);
772+
impl_num_cast_nonzero!(NonZeroU8, to_u8);
773+
impl_num_cast_nonzero!(NonZeroU16, to_u16);
774+
impl_num_cast_nonzero!(NonZeroU32, to_u32);
775+
impl_num_cast_nonzero!(NonZeroU64, to_u64);
776+
impl_num_cast_nonzero!(NonZeroU128, to_u128);
777+
778+
impl_num_cast_nonzero!(NonZeroIsize, to_isize);
779+
impl_num_cast_nonzero!(NonZeroI8, to_i8);
780+
impl_num_cast_nonzero!(NonZeroI16, to_i16);
781+
impl_num_cast_nonzero!(NonZeroI32, to_i32);
782+
impl_num_cast_nonzero!(NonZeroI64, to_i64);
783+
impl_num_cast_nonzero!(NonZeroI128, to_i128);
784+
757785
impl<T: NumCast> NumCast for Wrapping<T> {
758786
fn from<U: ToPrimitive>(n: U) -> Option<Self> {
759787
T::from(n).map(Wrapping)

tests/cast.rs

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
use num_traits::cast::*;
66
use num_traits::Bounded;
77

8+
use core::num::{
9+
NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
10+
NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize,
11+
};
812
use core::{f32, f64};
913
use core::{i128, i16, i32, i64, i8, isize};
1014
use core::{u128, u16, u32, u64, u8, usize};
@@ -101,17 +105,50 @@ fn cast_to_int_checks_overflow() {
101105
assert_eq!(None, cast::<f64, i32>(big_f));
102106
assert_eq!(None, cast::<f64, i64>(big_f));
103107

108+
assert_eq!(None, cast::<f64, NonZeroIsize>(big_f));
109+
assert_eq!(None, cast::<f64, NonZeroI8>(big_f));
110+
assert_eq!(None, cast::<f64, NonZeroI16>(big_f));
111+
assert_eq!(None, cast::<f64, NonZeroI32>(big_f));
112+
assert_eq!(None, cast::<f64, NonZeroI64>(big_f));
113+
104114
assert_eq!(Some(normal_f as isize), cast::<f64, isize>(normal_f));
105115
assert_eq!(Some(normal_f as i8), cast::<f64, i8>(normal_f));
106116
assert_eq!(Some(normal_f as i16), cast::<f64, i16>(normal_f));
107117
assert_eq!(Some(normal_f as i32), cast::<f64, i32>(normal_f));
108118
assert_eq!(Some(normal_f as i64), cast::<f64, i64>(normal_f));
109119

120+
assert_eq!(
121+
NonZeroIsize::new(normal_f as isize),
122+
cast::<f64, NonZeroIsize>(normal_f)
123+
);
124+
assert_eq!(
125+
NonZeroI8::new(normal_f as i8),
126+
cast::<f64, NonZeroI8>(normal_f)
127+
);
128+
assert_eq!(
129+
NonZeroI16::new(normal_f as i16),
130+
cast::<f64, NonZeroI16>(normal_f)
131+
);
132+
assert_eq!(
133+
NonZeroI32::new(normal_f as i32),
134+
cast::<f64, NonZeroI32>(normal_f)
135+
);
136+
assert_eq!(
137+
NonZeroI64::new(normal_f as i64),
138+
cast::<f64, NonZeroI64>(normal_f)
139+
);
140+
110141
assert_eq!(None, cast::<f64, isize>(small_f));
111142
assert_eq!(None, cast::<f64, i8>(small_f));
112143
assert_eq!(None, cast::<f64, i16>(small_f));
113144
assert_eq!(None, cast::<f64, i32>(small_f));
114145
assert_eq!(None, cast::<f64, i64>(small_f));
146+
147+
assert_eq!(None, cast::<f64, NonZeroIsize>(small_f));
148+
assert_eq!(None, cast::<f64, NonZeroI8>(small_f));
149+
assert_eq!(None, cast::<f64, NonZeroI16>(small_f));
150+
assert_eq!(None, cast::<f64, NonZeroI32>(small_f));
151+
assert_eq!(None, cast::<f64, NonZeroI64>(small_f));
115152
}
116153

117154
#[test]
@@ -125,17 +162,50 @@ fn cast_to_unsigned_int_checks_overflow() {
125162
assert_eq!(None, cast::<f64, u32>(big_f));
126163
assert_eq!(None, cast::<f64, u64>(big_f));
127164

165+
assert_eq!(None, cast::<f64, NonZeroUsize>(big_f));
166+
assert_eq!(None, cast::<f64, NonZeroU8>(big_f));
167+
assert_eq!(None, cast::<f64, NonZeroU16>(big_f));
168+
assert_eq!(None, cast::<f64, NonZeroU32>(big_f));
169+
assert_eq!(None, cast::<f64, NonZeroU64>(big_f));
170+
128171
assert_eq!(Some(normal_f as usize), cast::<f64, usize>(normal_f));
129172
assert_eq!(Some(normal_f as u8), cast::<f64, u8>(normal_f));
130173
assert_eq!(Some(normal_f as u16), cast::<f64, u16>(normal_f));
131174
assert_eq!(Some(normal_f as u32), cast::<f64, u32>(normal_f));
132175
assert_eq!(Some(normal_f as u64), cast::<f64, u64>(normal_f));
133176

177+
assert_eq!(
178+
NonZeroUsize::new(normal_f as usize),
179+
cast::<f64, NonZeroUsize>(normal_f)
180+
);
181+
assert_eq!(
182+
NonZeroU8::new(normal_f as u8),
183+
cast::<f64, NonZeroU8>(normal_f)
184+
);
185+
assert_eq!(
186+
NonZeroU16::new(normal_f as u16),
187+
cast::<f64, NonZeroU16>(normal_f)
188+
);
189+
assert_eq!(
190+
NonZeroU32::new(normal_f as u32),
191+
cast::<f64, NonZeroU32>(normal_f)
192+
);
193+
assert_eq!(
194+
NonZeroU64::new(normal_f as u64),
195+
cast::<f64, NonZeroU64>(normal_f)
196+
);
197+
134198
assert_eq!(None, cast::<f64, usize>(small_f));
135199
assert_eq!(None, cast::<f64, u8>(small_f));
136200
assert_eq!(None, cast::<f64, u16>(small_f));
137201
assert_eq!(None, cast::<f64, u32>(small_f));
138202
assert_eq!(None, cast::<f64, u64>(small_f));
203+
204+
assert_eq!(None, cast::<f64, NonZeroUsize>(small_f));
205+
assert_eq!(None, cast::<f64, NonZeroU8>(small_f));
206+
assert_eq!(None, cast::<f64, NonZeroU16>(small_f));
207+
assert_eq!(None, cast::<f64, NonZeroU32>(small_f));
208+
assert_eq!(None, cast::<f64, NonZeroU64>(small_f));
139209
}
140210

141211
#[test]
@@ -146,11 +216,26 @@ fn cast_to_i128_checks_overflow() {
146216
assert_eq!(None, cast::<f64, i128>(big_f));
147217
assert_eq!(None, cast::<f64, u128>(big_f));
148218

219+
assert_eq!(None, cast::<f64, NonZeroI128>(big_f));
220+
assert_eq!(None, cast::<f64, NonZeroU128>(big_f));
221+
149222
assert_eq!(Some(normal_f as i128), cast::<f64, i128>(normal_f));
150223
assert_eq!(Some(normal_f as u128), cast::<f64, u128>(normal_f));
151224

225+
assert_eq!(
226+
NonZeroI128::new(normal_f as i128),
227+
cast::<f64, NonZeroI128>(normal_f)
228+
);
229+
assert_eq!(
230+
NonZeroU128::new(normal_f as u128),
231+
cast::<f64, NonZeroU128>(normal_f)
232+
);
233+
152234
assert_eq!(None, cast::<f64, i128>(small_f));
153235
assert_eq!(None, cast::<f64, u128>(small_f));
236+
237+
assert_eq!(None, cast::<f64, NonZeroI128>(small_f));
238+
assert_eq!(None, cast::<f64, NonZeroU128>(small_f));
154239
}
155240

156241
#[cfg(feature = "std")]
@@ -310,6 +395,152 @@ fn cast_int_to_128_edge_cases() {
310395
test_edge!(usize u8 u16 u32 u64 u128);
311396
}
312397

398+
macro_rules! nonzero_to_int_test_edge {
399+
($f:ident -> { $($t:ident)+ } with $BigS:ident $BigU:ident ) => { $({
400+
#[allow(arithmetic_overflow)] // https://github.com/rust-lang/rust/issues/109731
401+
fn test_edge() {
402+
dbg!("testing cast edge cases for {} -> {}", stringify!($f), stringify!($t));
403+
404+
405+
let from_min = <$f as Bounded>::min_value();
406+
match (from_min.get() as $BigS).cmp(&($t::MIN as $BigS)) {
407+
Greater => {
408+
assert_eq!(Some(from_min.get() as $t), cast::<$f, $t>(from_min));
409+
}
410+
Equal => {
411+
assert_eq!(Some($t::MIN), cast::<$f, $t>(from_min));
412+
}
413+
Less => {
414+
if $t::MIN != 0 {
415+
let min = $f::new($t::MIN.as_()).unwrap();
416+
assert_eq!(Some($t::MIN), cast::<$f, $t>(min));
417+
assert_eq!(None, cast::<$f, $t>($f::new(min.get() - 1).unwrap()));
418+
}
419+
}
420+
}
421+
422+
let from_max = <$f as Bounded>::max_value();
423+
match (from_max.get() as $BigU).cmp(&($t::MAX as $BigU)) {
424+
Greater => {
425+
let max = $f::new($t::MAX.as_()).unwrap();
426+
assert_eq!(Some($t::MAX), cast::<$f, $t>(max));
427+
assert_eq!(None, cast::<$f, $t>($f::new(max.get() + 1).unwrap()));
428+
}
429+
Equal => {
430+
assert_eq!(Some($t::MAX), cast::<$f, $t>(from_max));
431+
}
432+
Less => {
433+
assert_eq!(Some(from_max.get() as $t), cast::<$f, $t>(from_max));
434+
}
435+
}
436+
}
437+
test_edge();
438+
})+}
439+
}
440+
441+
#[test]
442+
fn cast_nonzero_to_int_edge_cases() {
443+
use core::cmp::Ordering::*;
444+
445+
macro_rules! test_edge {
446+
($( $from:ident )+) => { $({
447+
nonzero_to_int_test_edge!($from -> { isize i8 i16 i32 i64 } with i64 u64);
448+
nonzero_to_int_test_edge!($from -> { usize u8 u16 u32 u64 } with i64 u64);
449+
})+}
450+
}
451+
452+
test_edge!(NonZeroIsize NonZeroI8 NonZeroI16 NonZeroI32 NonZeroI64);
453+
test_edge!(NonZeroUsize NonZeroU8 NonZeroU16 NonZeroU32 NonZeroU64);
454+
}
455+
456+
#[test]
457+
fn cast_nonzero_to_128_edge_cases() {
458+
use core::cmp::Ordering::*;
459+
460+
macro_rules! test_edge {
461+
($( $t:ident )+) => {
462+
$(
463+
nonzero_to_int_test_edge!($t -> { i128 u128 } with i128 u128);
464+
)+
465+
}
466+
}
467+
468+
test_edge!(NonZeroIsize NonZeroI8 NonZeroI16 NonZeroI32 NonZeroI64 NonZeroI128);
469+
test_edge!(NonZeroUsize NonZeroU8 NonZeroU16 NonZeroU32 NonZeroU64 NonZeroU128);
470+
}
471+
472+
macro_rules! int_to_nonzero_test_edge {
473+
($f:ident -> { $($t:ident)+ } with $BigS:ident $BigU:ident ) => { $({
474+
#[allow(arithmetic_overflow)] // https://github.com/rust-lang/rust/issues/109731
475+
fn test_edge() {
476+
dbg!("testing cast edge cases for {} -> {}", stringify!($f), stringify!($t));
477+
478+
let target_min = <$t as Bounded>::min_value();
479+
match ($f::MIN as $BigS).cmp(&(target_min.get() as $BigS)) {
480+
Greater => {
481+
assert_eq!($t::new($f::MIN.as_()), cast::<$f, $t>($f::MIN));
482+
}
483+
Equal => {
484+
assert_eq!(Some(target_min), cast::<$f, $t>($f::MIN));
485+
}
486+
Less => {
487+
let min = target_min.get() as $f;
488+
assert_eq!(Some(target_min), cast::<$f, $t>(min));
489+
assert_eq!(None, cast::<$f, $t>(min - 1));
490+
}
491+
}
492+
493+
let target_max = <$t as Bounded>::max_value();
494+
match ($f::MAX as $BigU).cmp(&($t::max_value().get() as $BigU)) {
495+
Greater => {
496+
let max = target_max.get() as $f;
497+
assert_eq!(Some(target_max), cast::<$f, $t>(max));
498+
assert_eq!(None, cast::<$f, $t>(max + 1));
499+
}
500+
Equal => {
501+
assert_eq!(Some(target_max), cast::<$f, $t>($f::MAX));
502+
}
503+
Less => {
504+
assert_eq!($t::new($f::MAX.as_()), cast::<$f, $t>($f::MAX));
505+
}
506+
}
507+
}
508+
test_edge();
509+
})+}
510+
}
511+
512+
#[test]
513+
fn cast_int_to_nonzero_edge_cases() {
514+
use core::cmp::Ordering::*;
515+
516+
macro_rules! test_edge {
517+
($( $from:ident )+) => { $({
518+
int_to_nonzero_test_edge!($from -> { NonZeroIsize NonZeroI8 NonZeroI16 NonZeroI32 NonZeroI64 } with i64 u64);
519+
int_to_nonzero_test_edge!($from -> { NonZeroUsize NonZeroU8 NonZeroU16 NonZeroU32 NonZeroU64 } with i64 u64);
520+
})+}
521+
}
522+
523+
test_edge!(isize i8 i16 i32 i64);
524+
test_edge!(usize u8 u16 u32 u64);
525+
}
526+
527+
#[test]
528+
fn cast_128_to_nonzero_edge_cases() {
529+
use core::cmp::Ordering::*;
530+
531+
macro_rules! test_edge {
532+
($( $t:ident )+) => {
533+
$(
534+
int_to_nonzero_test_edge!($t -> { NonZeroIsize NonZeroI8 NonZeroI16 NonZeroI32 NonZeroI64 NonZeroI128 } with i128 u128);
535+
int_to_nonzero_test_edge!($t -> { NonZeroUsize NonZeroU8 NonZeroU16 NonZeroU32 NonZeroU64 NonZeroU128 } with i128 u128);
536+
)+
537+
}
538+
}
539+
540+
test_edge!(isize i8 i16 i32 i64 i128);
541+
test_edge!(usize u8 u16 u32 u64 u128);
542+
}
543+
313544
#[test]
314545
fn newtype_from_primitive() {
315546
#[derive(PartialEq, Debug)]

0 commit comments

Comments
 (0)