Skip to content

Commit 718b7a9

Browse files
committed
feat(core): impl Step for NonZero<u*>
Implement Step for NonZero unsigned integers as discussed in [libs-team#130][1]. [1]: rust-lang/libs-team#130 `step_nonzero_impls` was adapted from `step_integer_impls` and the tests were adapted from the step tests.
1 parent f25e92b commit 718b7a9

File tree

2 files changed

+200
-0
lines changed

2 files changed

+200
-0
lines changed

library/core/src/iter/range.rs

+133
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ macro_rules! unsafe_impl_trusted_step {
1616
)*};
1717
}
1818
unsafe_impl_trusted_step![AsciiChar char i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize Ipv4Addr Ipv6Addr];
19+
unsafe_impl_trusted_step![NonZero<u8> NonZero<u16> NonZero<u32> NonZero<u64> NonZero<u128> NonZero<usize>];
1920

2021
/// Objects that have a notion of *successor* and *predecessor* operations.
2122
///
@@ -431,6 +432,138 @@ step_integer_impls! {
431432
wider than usize: [u32 i32], [u64 i64], [u128 i128];
432433
}
433434

435+
// These are still macro-generated because the integer literals resolve to different types.
436+
macro_rules! step_nonzero_identical_methods {
437+
($int:ident) => {
438+
#[inline]
439+
unsafe fn forward_unchecked(start: Self, n: usize) -> Self {
440+
// SAFETY: the caller has to guarantee that `start + n` doesn't overflow.
441+
unsafe { Self::new_unchecked(start.get().unchecked_add(n as $int)) }
442+
}
443+
444+
#[inline]
445+
unsafe fn backward_unchecked(start: Self, n: usize) -> Self {
446+
// SAFETY: the caller has to guarantee that `start - n` doesn't overflow or hit zero.
447+
unsafe { Self::new_unchecked(start.get().unchecked_sub(n as $int)) }
448+
}
449+
450+
#[inline]
451+
#[allow(arithmetic_overflow)]
452+
#[rustc_inherit_overflow_checks]
453+
fn forward(start: Self, n: usize) -> Self {
454+
// In debug builds, trigger a panic on overflow.
455+
// This should optimize completely out in release builds.
456+
if Self::forward_checked(start, n).is_none() {
457+
let _ = $int::MAX + 1;
458+
}
459+
// Do saturating math (wrapping math causes UB if it wraps to Zero)
460+
start.saturating_add(n as $int)
461+
}
462+
463+
#[inline]
464+
#[allow(arithmetic_overflow)]
465+
#[rustc_inherit_overflow_checks]
466+
fn backward(start: Self, n: usize) -> Self {
467+
// In debug builds, trigger a panic on overflow.
468+
// This should optimize completely out in release builds.
469+
if Self::backward_checked(start, n).is_none() {
470+
let _ = $int::MIN - 1;
471+
}
472+
// Do saturating math (wrapping math causes UB if it wraps to Zero)
473+
Self::new(start.get().saturating_sub(n as $int)).unwrap_or(Self::MIN)
474+
}
475+
};
476+
}
477+
478+
macro_rules! step_nonzero_impls {
479+
{
480+
narrower than or same width as usize:
481+
$( $narrower:ident ),+;
482+
wider than usize:
483+
$( $wider:ident ),+;
484+
} => {
485+
$(
486+
#[allow(unreachable_patterns)]
487+
#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
488+
impl Step for NonZero<$narrower> {
489+
step_nonzero_identical_methods!($narrower);
490+
491+
#[inline]
492+
fn steps_between(start: &Self, end: &Self) -> Option<usize> {
493+
if *start <= *end {
494+
// This relies on $u_narrower <= usize
495+
Some((end.get() - start.get()) as usize)
496+
} else {
497+
None
498+
}
499+
}
500+
501+
#[inline]
502+
fn forward_checked(start: Self, n: usize) -> Option<Self> {
503+
match $narrower::try_from(n) {
504+
Ok(n) => start.checked_add(n),
505+
Err(_) => None, // if n is out of range, `unsigned_start + n` is too
506+
}
507+
}
508+
509+
#[inline]
510+
fn backward_checked(start: Self, n: usize) -> Option<Self> {
511+
match $narrower::try_from(n) {
512+
// *_sub() is not implemented on NonZero<T>
513+
Ok(n) => start.get().checked_sub(n).and_then(Self::new),
514+
Err(_) => None, // if n is out of range, `unsigned_start - n` is too
515+
}
516+
}
517+
}
518+
)+
519+
520+
$(
521+
#[allow(unreachable_patterns)]
522+
#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
523+
impl Step for NonZero<$wider> {
524+
step_nonzero_identical_methods!($wider);
525+
526+
#[inline]
527+
fn steps_between(start: &Self, end: &Self) -> Option<usize> {
528+
if *start <= *end {
529+
usize::try_from(end.get() - start.get()).ok()
530+
} else {
531+
None
532+
}
533+
}
534+
535+
#[inline]
536+
fn forward_checked(start: Self, n: usize) -> Option<Self> {
537+
start.checked_add(n as $wider)
538+
}
539+
540+
#[inline]
541+
fn backward_checked(start: Self, n: usize) -> Option<Self> {
542+
start.get().checked_sub(n as $wider).and_then(Self::new)
543+
}
544+
}
545+
)+
546+
};
547+
}
548+
549+
#[cfg(target_pointer_width = "64")]
550+
step_nonzero_impls! {
551+
narrower than or same width as usize: u8, u16, u32, u64, usize;
552+
wider than usize: u128;
553+
}
554+
555+
#[cfg(target_pointer_width = "32")]
556+
step_nonzero_impls! {
557+
narrower than or same width as usize: u8, u16, u32, usize;
558+
wider than usize: u64, u128;
559+
}
560+
561+
#[cfg(target_pointer_width = "16")]
562+
step_nonzero_impls! {
563+
narrower than or same width as usize: u8, u16, usize;
564+
wider than usize: u32, u64, u128;
565+
}
566+
434567
#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
435568
impl Step for char {
436569
#[inline]

library/core/tests/iter/range.rs

+67
Original file line numberDiff line numberDiff line change
@@ -501,3 +501,70 @@ fn test_double_ended_range() {
501501
panic!("unreachable");
502502
}
503503
}
504+
505+
macro_rules! nz {
506+
($type:ident($val:literal)) => {
507+
$type::new($val).unwrap()
508+
};
509+
}
510+
511+
macro_rules! nonzero_array {
512+
($type:ident[$($val:literal),*]) => {
513+
[$(nz!($type($val))),*]
514+
}
515+
}
516+
517+
macro_rules! nonzero_range {
518+
($type:ident($($left:literal)?..$($right:literal)?)) => {
519+
nz!($type($($left)?))..nz!($type($($right)?))
520+
};
521+
($type:ident($($left:literal)?..=$right:literal)) => {
522+
nz!($type($($left)?))..=nz!($type($right))
523+
};
524+
}
525+
526+
#[test]
527+
fn test_nonzero_range() {
528+
#![allow(deprecated)]
529+
use core::num::{NonZeroU32, NonZeroU8, NonZeroUsize};
530+
531+
assert_eq!(
532+
nonzero_range!(NonZeroUsize(1..=21)).step_by(5).collect::<Vec<_>>(),
533+
nonzero_array!(NonZeroUsize[1, 6, 11, 16, 21])
534+
);
535+
assert_eq!(
536+
nonzero_range!(NonZeroUsize(1..=20)).step_by(5).collect::<Vec<_>>(),
537+
nonzero_array!(NonZeroUsize[1, 6, 11, 16])
538+
);
539+
assert_eq!(
540+
nonzero_range!(NonZeroUsize(1..20)).step_by(5).collect::<Vec<_>>(),
541+
nonzero_array!(NonZeroUsize[1, 6, 11, 16])
542+
);
543+
assert_eq!(
544+
nonzero_range!(NonZeroUsize(1..21)).rev().step_by(5).collect::<Vec<_>>(),
545+
nonzero_array!(NonZeroUsize[20, 15, 10, 5])
546+
);
547+
assert_eq!(
548+
nonzero_range!(NonZeroUsize(1..21)).rev().step_by(6).collect::<Vec<_>>(),
549+
nonzero_array!(NonZeroUsize[20, 14, 8, 2])
550+
);
551+
assert_eq!(
552+
nonzero_range!(NonZeroU8(200..255)).step_by(50).collect::<Vec<_>>(),
553+
nonzero_array!(NonZeroU8[200, 250])
554+
);
555+
assert_eq!(
556+
nonzero_range!(NonZeroUsize(200..5)).step_by(1).collect::<Vec<_>>(),
557+
nonzero_array!(NonZeroUsize[])
558+
);
559+
assert_eq!(
560+
nonzero_range!(NonZeroUsize(200..200)).step_by(1).collect::<Vec<_>>(),
561+
nonzero_array!(NonZeroUsize[])
562+
);
563+
564+
assert_eq!(nonzero_range!(NonZeroU32(10..20)).step_by(1).size_hint(), (10, Some(10)));
565+
assert_eq!(nonzero_range!(NonZeroU32(10..20)).step_by(5).size_hint(), (2, Some(2)));
566+
assert_eq!(nonzero_range!(NonZeroU32(1..21)).rev().step_by(5).size_hint(), (4, Some(4)));
567+
assert_eq!(nonzero_range!(NonZeroU32(1..21)).rev().step_by(6).size_hint(), (4, Some(4)));
568+
assert_eq!(nonzero_range!(NonZeroU32(20..1)).step_by(1).size_hint(), (0, Some(0)));
569+
assert_eq!(nonzero_range!(NonZeroU32(20..20)).step_by(1).size_hint(), (0, Some(0)));
570+
}

0 commit comments

Comments
 (0)