feat: Implement int_from_ascii for NonZero<T>

This commit is contained in:
Shun Sakai 2026-02-07 13:42:49 +09:00
parent c58d9f9f82
commit d837cf6700
3 changed files with 166 additions and 11 deletions

View file

@ -1241,6 +1241,114 @@ macro_rules! nonzero_integer {
unsafe { Self::new_unchecked(self.get().saturating_pow(other)) }
}
/// Parses a non-zero integer from an ASCII-byte slice with decimal digits.
///
/// The characters are expected to be an optional
#[doc = sign_dependent_expr!{
$signedness ?
if signed {
" `+` or `-` "
}
if unsigned {
" `+` "
}
}]
/// sign followed by only digits. Leading and trailing non-digit characters (including
/// whitespace) represent an error. Underscores (which are accepted in Rust literals)
/// also represent an error.
///
/// # Examples
///
/// ```
/// #![feature(int_from_ascii)]
///
/// # use std::num::NonZero;
/// #
/// # fn main() { test().unwrap(); }
/// # fn test() -> Option<()> {
#[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::from_ascii(b\"+10\"), Ok(NonZero::new(10)?));")]
/// # Some(())
/// # }
/// ```
///
/// Trailing space returns error:
///
/// ```
/// #![feature(int_from_ascii)]
///
/// # use std::num::NonZero;
/// #
#[doc = concat!("assert!(NonZero::<", stringify!($Int), ">::from_ascii(b\"1 \").is_err());")]
/// ```
#[unstable(feature = "int_from_ascii", issue = "134821")]
#[inline]
pub const fn from_ascii(src: &[u8]) -> Result<Self, ParseIntError> {
Self::from_ascii_radix(src, 10)
}
/// Parses a non-zero integer from an ASCII-byte slice with digits in a given base.
///
/// The characters are expected to be an optional
#[doc = sign_dependent_expr!{
$signedness ?
if signed {
" `+` or `-` "
}
if unsigned {
" `+` "
}
}]
/// sign followed by only digits. Leading and trailing non-digit characters (including
/// whitespace) represent an error. Underscores (which are accepted in Rust literals)
/// also represent an error.
///
/// Digits are a subset of these characters, depending on `radix`:
///
/// - `0-9`
/// - `a-z`
/// - `A-Z`
///
/// # Panics
///
/// This method panics if `radix` is not in the range from 2 to 36.
///
/// # Examples
///
/// ```
/// #![feature(int_from_ascii)]
///
/// # use std::num::NonZero;
/// #
/// # fn main() { test().unwrap(); }
/// # fn test() -> Option<()> {
#[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::from_ascii_radix(b\"A\", 16), Ok(NonZero::new(10)?));")]
/// # Some(())
/// # }
/// ```
///
/// Trailing space returns error:
///
/// ```
/// #![feature(int_from_ascii)]
///
/// # use std::num::NonZero;
/// #
#[doc = concat!("assert!(NonZero::<", stringify!($Int), ">::from_ascii_radix(b\"1 \", 10).is_err());")]
/// ```
#[unstable(feature = "int_from_ascii", issue = "134821")]
#[inline]
pub const fn from_ascii_radix(src: &[u8], radix: u32) -> Result<Self, ParseIntError> {
let n = match <$Int>::from_ascii_radix(src, radix) {
Ok(n) => n,
Err(err) => return Err(err),
};
if let Some(n) = Self::new(n) {
Ok(n)
} else {
Err(ParseIntError { kind: IntErrorKind::Zero })
}
}
/// Parses a non-zero integer from a string slice with digits in a given base.
///
/// The string is expected to be an optional
@ -1269,8 +1377,6 @@ macro_rules! nonzero_integer {
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(nonzero_from_str_radix)]
///
@ -1295,15 +1401,7 @@ macro_rules! nonzero_integer {
#[unstable(feature = "nonzero_from_str_radix", issue = "152193")]
#[inline]
pub const fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseIntError> {
let n = match <$Int>::from_str_radix(src, radix) {
Ok(n) => n,
Err(err) => return Err(err),
};
if let Some(n) = Self::new(n) {
Ok(n)
} else {
Err(ParseIntError { kind: IntErrorKind::Zero })
}
Self::from_ascii_radix(src.as_bytes(), radix)
}
}

View file

@ -66,6 +66,7 @@
#![feature(generic_assert_internals)]
#![feature(hasher_prefixfree_extras)]
#![feature(hashmap_internals)]
#![feature(int_from_ascii)]
#![feature(int_lowest_highest_one)]
#![feature(int_roundings)]
#![feature(ip)]

View file

@ -124,6 +124,62 @@ fn test_from_signed_nonzero() {
assert_eq!(num, 1i32);
}
#[test]
fn test_from_ascii_radix() {
assert_eq!(NonZero::<u8>::from_ascii_radix(b"123", 10), Ok(NonZero::new(123).unwrap()));
assert_eq!(NonZero::<u8>::from_ascii_radix(b"1001", 2), Ok(NonZero::new(9).unwrap()));
assert_eq!(NonZero::<u8>::from_ascii_radix(b"123", 8), Ok(NonZero::new(83).unwrap()));
assert_eq!(NonZero::<u16>::from_ascii_radix(b"123", 16), Ok(NonZero::new(291).unwrap()));
assert_eq!(NonZero::<u16>::from_ascii_radix(b"ffff", 16), Ok(NonZero::new(65535).unwrap()));
assert_eq!(NonZero::<u8>::from_ascii_radix(b"z", 36), Ok(NonZero::new(35).unwrap()));
assert_eq!(
NonZero::<u8>::from_ascii_radix(b"0", 10).err().map(|e| e.kind().clone()),
Some(IntErrorKind::Zero)
);
assert_eq!(
NonZero::<u8>::from_ascii_radix(b"-1", 10).err().map(|e| e.kind().clone()),
Some(IntErrorKind::InvalidDigit)
);
assert_eq!(
NonZero::<i8>::from_ascii_radix(b"-129", 10).err().map(|e| e.kind().clone()),
Some(IntErrorKind::NegOverflow)
);
assert_eq!(
NonZero::<u8>::from_ascii_radix(b"257", 10).err().map(|e| e.kind().clone()),
Some(IntErrorKind::PosOverflow)
);
assert_eq!(
NonZero::<u8>::from_ascii_radix(b"Z", 10).err().map(|e| e.kind().clone()),
Some(IntErrorKind::InvalidDigit)
);
assert_eq!(
NonZero::<u8>::from_ascii_radix(b"_", 2).err().map(|e| e.kind().clone()),
Some(IntErrorKind::InvalidDigit)
);
}
#[test]
fn test_from_ascii() {
assert_eq!(NonZero::<u8>::from_ascii(b"123"), Ok(NonZero::new(123).unwrap()));
assert_eq!(
NonZero::<u8>::from_ascii(b"0").err().map(|e| e.kind().clone()),
Some(IntErrorKind::Zero)
);
assert_eq!(
NonZero::<u8>::from_ascii(b"-1").err().map(|e| e.kind().clone()),
Some(IntErrorKind::InvalidDigit)
);
assert_eq!(
NonZero::<i8>::from_ascii(b"-129").err().map(|e| e.kind().clone()),
Some(IntErrorKind::NegOverflow)
);
assert_eq!(
NonZero::<u8>::from_ascii(b"257").err().map(|e| e.kind().clone()),
Some(IntErrorKind::PosOverflow)
);
}
#[test]
fn test_from_str_radix() {
assert_eq!(NonZero::<u8>::from_str_radix("123", 10), Ok(NonZero::new(123).unwrap()));