Skip to content

Commit 4769191

Browse files
Rollup merge of rust-lang#152267 - sorairolake:feature/nonzero-from-ascii, r=dtolnay
feat: Implement `int_from_ascii` for `NonZero<T>` - Tracking issue: rust-lang#134821 This pull request adds `from_ascii` and `from_ascii_radix` methods to `NonZero<T>` that parses a non-zero integer from an ASCII-byte slice (`&[u8]`) with decimal digits or digits in a given base. When using the combination of `int::from_ascii` or `int::from_ascii_radix` and `NonZero::<T>::new`, [`IntErrorKind::Zero`](https://doc.rust-lang.org/core/num/enum.IntErrorKind.html#variant.Zero) cannot be returned as an error. `NonZero::<T>::from_str_radix` and `NonZero::<T>::from_str` require a string (`&str`) as a parameter. ```rust // Cannot return `IntErrorKind::Zero` as an error. assert_eq!(NonZero::new(u8::from_ascii(b"0").unwrap()), None); // Can return `IntErrorKind::Zero` as an error. let err = NonZero::<u8>::from_ascii(b"0").unwrap_err(); assert_eq!(err.kind(), &IntErrorKind::Zero); ``` See also rust-lang#152193
2 parents c600797 + d837cf6 commit 4769191

File tree

3 files changed

+166
-11
lines changed

3 files changed

+166
-11
lines changed

library/core/src/num/nonzero.rs

Lines changed: 109 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,9 +1241,54 @@ macro_rules! nonzero_integer {
12411241
unsafe { Self::new_unchecked(self.get().saturating_pow(other)) }
12421242
}
12431243

1244-
/// Parses a non-zero integer from a string slice with digits in a given base.
1244+
/// Parses a non-zero integer from an ASCII-byte slice with decimal digits.
12451245
///
1246-
/// The string is expected to be an optional
1246+
/// The characters are expected to be an optional
1247+
#[doc = sign_dependent_expr!{
1248+
$signedness ?
1249+
if signed {
1250+
" `+` or `-` "
1251+
}
1252+
if unsigned {
1253+
" `+` "
1254+
}
1255+
}]
1256+
/// sign followed by only digits. Leading and trailing non-digit characters (including
1257+
/// whitespace) represent an error. Underscores (which are accepted in Rust literals)
1258+
/// also represent an error.
1259+
///
1260+
/// # Examples
1261+
///
1262+
/// ```
1263+
/// #![feature(int_from_ascii)]
1264+
///
1265+
/// # use std::num::NonZero;
1266+
/// #
1267+
/// # fn main() { test().unwrap(); }
1268+
/// # fn test() -> Option<()> {
1269+
#[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::from_ascii(b\"+10\"), Ok(NonZero::new(10)?));")]
1270+
/// # Some(())
1271+
/// # }
1272+
/// ```
1273+
///
1274+
/// Trailing space returns error:
1275+
///
1276+
/// ```
1277+
/// #![feature(int_from_ascii)]
1278+
///
1279+
/// # use std::num::NonZero;
1280+
/// #
1281+
#[doc = concat!("assert!(NonZero::<", stringify!($Int), ">::from_ascii(b\"1 \").is_err());")]
1282+
/// ```
1283+
#[unstable(feature = "int_from_ascii", issue = "134821")]
1284+
#[inline]
1285+
pub const fn from_ascii(src: &[u8]) -> Result<Self, ParseIntError> {
1286+
Self::from_ascii_radix(src, 10)
1287+
}
1288+
1289+
/// Parses a non-zero integer from an ASCII-byte slice with digits in a given base.
1290+
///
1291+
/// The characters are expected to be an optional
12471292
#[doc = sign_dependent_expr!{
12481293
$signedness ?
12491294
if signed {
@@ -1269,33 +1314,31 @@ macro_rules! nonzero_integer {
12691314
///
12701315
/// # Examples
12711316
///
1272-
/// Basic usage:
1273-
///
12741317
/// ```
1275-
/// #![feature(nonzero_from_str_radix)]
1318+
/// #![feature(int_from_ascii)]
12761319
///
12771320
/// # use std::num::NonZero;
12781321
/// #
12791322
/// # fn main() { test().unwrap(); }
12801323
/// # fn test() -> Option<()> {
1281-
#[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::from_str_radix(\"A\", 16), Ok(NonZero::new(10)?));")]
1324+
#[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::from_ascii_radix(b\"A\", 16), Ok(NonZero::new(10)?));")]
12821325
/// # Some(())
12831326
/// # }
12841327
/// ```
12851328
///
12861329
/// Trailing space returns error:
12871330
///
12881331
/// ```
1289-
/// #![feature(nonzero_from_str_radix)]
1332+
/// #![feature(int_from_ascii)]
12901333
///
12911334
/// # use std::num::NonZero;
12921335
/// #
1293-
#[doc = concat!("assert!(NonZero::<", stringify!($Int), ">::from_str_radix(\"1 \", 10).is_err());")]
1336+
#[doc = concat!("assert!(NonZero::<", stringify!($Int), ">::from_ascii_radix(b\"1 \", 10).is_err());")]
12941337
/// ```
1295-
#[unstable(feature = "nonzero_from_str_radix", issue = "152193")]
1338+
#[unstable(feature = "int_from_ascii", issue = "134821")]
12961339
#[inline]
1297-
pub const fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseIntError> {
1298-
let n = match <$Int>::from_str_radix(src, radix) {
1340+
pub const fn from_ascii_radix(src: &[u8], radix: u32) -> Result<Self, ParseIntError> {
1341+
let n = match <$Int>::from_ascii_radix(src, radix) {
12991342
Ok(n) => n,
13001343
Err(err) => return Err(err),
13011344
};
@@ -1305,6 +1348,61 @@ macro_rules! nonzero_integer {
13051348
Err(ParseIntError { kind: IntErrorKind::Zero })
13061349
}
13071350
}
1351+
1352+
/// Parses a non-zero integer from a string slice with digits in a given base.
1353+
///
1354+
/// The string is expected to be an optional
1355+
#[doc = sign_dependent_expr!{
1356+
$signedness ?
1357+
if signed {
1358+
" `+` or `-` "
1359+
}
1360+
if unsigned {
1361+
" `+` "
1362+
}
1363+
}]
1364+
/// sign followed by only digits. Leading and trailing non-digit characters (including
1365+
/// whitespace) represent an error. Underscores (which are accepted in Rust literals)
1366+
/// also represent an error.
1367+
///
1368+
/// Digits are a subset of these characters, depending on `radix`:
1369+
///
1370+
/// - `0-9`
1371+
/// - `a-z`
1372+
/// - `A-Z`
1373+
///
1374+
/// # Panics
1375+
///
1376+
/// This method panics if `radix` is not in the range from 2 to 36.
1377+
///
1378+
/// # Examples
1379+
///
1380+
/// ```
1381+
/// #![feature(nonzero_from_str_radix)]
1382+
///
1383+
/// # use std::num::NonZero;
1384+
/// #
1385+
/// # fn main() { test().unwrap(); }
1386+
/// # fn test() -> Option<()> {
1387+
#[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::from_str_radix(\"A\", 16), Ok(NonZero::new(10)?));")]
1388+
/// # Some(())
1389+
/// # }
1390+
/// ```
1391+
///
1392+
/// Trailing space returns error:
1393+
///
1394+
/// ```
1395+
/// #![feature(nonzero_from_str_radix)]
1396+
///
1397+
/// # use std::num::NonZero;
1398+
/// #
1399+
#[doc = concat!("assert!(NonZero::<", stringify!($Int), ">::from_str_radix(\"1 \", 10).is_err());")]
1400+
/// ```
1401+
#[unstable(feature = "nonzero_from_str_radix", issue = "152193")]
1402+
#[inline]
1403+
pub const fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseIntError> {
1404+
Self::from_ascii_radix(src.as_bytes(), radix)
1405+
}
13081406
}
13091407

13101408
#[stable(feature = "nonzero_parse", since = "1.35.0")]

library/coretests/tests/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
#![feature(generic_assert_internals)]
6767
#![feature(hasher_prefixfree_extras)]
6868
#![feature(hashmap_internals)]
69+
#![feature(int_from_ascii)]
6970
#![feature(int_lowest_highest_one)]
7071
#![feature(int_roundings)]
7172
#![feature(ip)]

library/coretests/tests/nonzero.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,62 @@ fn test_from_signed_nonzero() {
124124
assert_eq!(num, 1i32);
125125
}
126126

127+
#[test]
128+
fn test_from_ascii_radix() {
129+
assert_eq!(NonZero::<u8>::from_ascii_radix(b"123", 10), Ok(NonZero::new(123).unwrap()));
130+
assert_eq!(NonZero::<u8>::from_ascii_radix(b"1001", 2), Ok(NonZero::new(9).unwrap()));
131+
assert_eq!(NonZero::<u8>::from_ascii_radix(b"123", 8), Ok(NonZero::new(83).unwrap()));
132+
assert_eq!(NonZero::<u16>::from_ascii_radix(b"123", 16), Ok(NonZero::new(291).unwrap()));
133+
assert_eq!(NonZero::<u16>::from_ascii_radix(b"ffff", 16), Ok(NonZero::new(65535).unwrap()));
134+
assert_eq!(NonZero::<u8>::from_ascii_radix(b"z", 36), Ok(NonZero::new(35).unwrap()));
135+
assert_eq!(
136+
NonZero::<u8>::from_ascii_radix(b"0", 10).err().map(|e| e.kind().clone()),
137+
Some(IntErrorKind::Zero)
138+
);
139+
assert_eq!(
140+
NonZero::<u8>::from_ascii_radix(b"-1", 10).err().map(|e| e.kind().clone()),
141+
Some(IntErrorKind::InvalidDigit)
142+
);
143+
assert_eq!(
144+
NonZero::<i8>::from_ascii_radix(b"-129", 10).err().map(|e| e.kind().clone()),
145+
Some(IntErrorKind::NegOverflow)
146+
);
147+
assert_eq!(
148+
NonZero::<u8>::from_ascii_radix(b"257", 10).err().map(|e| e.kind().clone()),
149+
Some(IntErrorKind::PosOverflow)
150+
);
151+
152+
assert_eq!(
153+
NonZero::<u8>::from_ascii_radix(b"Z", 10).err().map(|e| e.kind().clone()),
154+
Some(IntErrorKind::InvalidDigit)
155+
);
156+
assert_eq!(
157+
NonZero::<u8>::from_ascii_radix(b"_", 2).err().map(|e| e.kind().clone()),
158+
Some(IntErrorKind::InvalidDigit)
159+
);
160+
}
161+
162+
#[test]
163+
fn test_from_ascii() {
164+
assert_eq!(NonZero::<u8>::from_ascii(b"123"), Ok(NonZero::new(123).unwrap()));
165+
assert_eq!(
166+
NonZero::<u8>::from_ascii(b"0").err().map(|e| e.kind().clone()),
167+
Some(IntErrorKind::Zero)
168+
);
169+
assert_eq!(
170+
NonZero::<u8>::from_ascii(b"-1").err().map(|e| e.kind().clone()),
171+
Some(IntErrorKind::InvalidDigit)
172+
);
173+
assert_eq!(
174+
NonZero::<i8>::from_ascii(b"-129").err().map(|e| e.kind().clone()),
175+
Some(IntErrorKind::NegOverflow)
176+
);
177+
assert_eq!(
178+
NonZero::<u8>::from_ascii(b"257").err().map(|e| e.kind().clone()),
179+
Some(IntErrorKind::PosOverflow)
180+
);
181+
}
182+
127183
#[test]
128184
fn test_from_str_radix() {
129185
assert_eq!(NonZero::<u8>::from_str_radix("123", 10), Ok(NonZero::new(123).unwrap()));

0 commit comments

Comments
 (0)