Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit e506248

Browse files
committedJan 30, 2025
Auto merge of #134824 - niklasf:int_from_ascii, r=ibraheemdev
Implement `int_from_ascii` (#134821) Provides unstable `T::from_ascii()` and `T::from_ascii_radix()` for integer types `T`, as drafted in tracking issue #134821. To deduplicate documentation without additional macros, implementations of `isize` and `usize` no longer delegate to equivalent integer types. After #132870 they are inlined anyway.
2 parents e6f12c8 + 7d0518c commit e506248

File tree

2 files changed

+139
-93
lines changed

2 files changed

+139
-93
lines changed
 

‎library/core/src/num/mod.rs

+131-89
Original file line numberDiff line numberDiff line change
@@ -1322,20 +1322,6 @@ pub enum FpCategory {
13221322
Normal,
13231323
}
13241324

1325-
macro_rules! from_str_radix_int_impl {
1326-
($($t:ty)*) => {$(
1327-
#[stable(feature = "rust1", since = "1.0.0")]
1328-
impl FromStr for $t {
1329-
type Err = ParseIntError;
1330-
#[inline]
1331-
fn from_str(src: &str) -> Result<Self, ParseIntError> {
1332-
<$t>::from_str_radix(src, 10)
1333-
}
1334-
}
1335-
)*}
1336-
}
1337-
from_str_radix_int_impl! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 }
1338-
13391325
/// Determines if a string of text of that length of that radix could be guaranteed to be
13401326
/// stored in the given type T.
13411327
/// Note that if the radix is known to the compiler, it is just the check of digits.len that
@@ -1351,18 +1337,58 @@ pub const fn can_not_overflow<T>(radix: u32, is_signed_ty: bool, digits: &[u8])
13511337
#[cfg_attr(feature = "panic_immediate_abort", inline)]
13521338
#[cold]
13531339
#[track_caller]
1354-
const fn from_str_radix_panic(radix: u32) -> ! {
1340+
const fn from_ascii_radix_panic(radix: u32) -> ! {
13551341
const_panic!(
1356-
"from_str_radix_int: must lie in the range `[2, 36]`",
1357-
"from_str_radix_int: must lie in the range `[2, 36]` - found {radix}",
1342+
"from_ascii_radix: radix must lie in the range `[2, 36]`",
1343+
"from_ascii_radix: radix must lie in the range `[2, 36]` - found {radix}",
13581344
radix: u32 = radix,
13591345
)
13601346
}
13611347

1362-
macro_rules! from_str_radix {
1348+
macro_rules! from_str_int_impl {
13631349
($signedness:ident $($int_ty:ty)+) => {$(
1350+
#[stable(feature = "rust1", since = "1.0.0")]
1351+
impl FromStr for $int_ty {
1352+
type Err = ParseIntError;
1353+
1354+
/// Parses an integer from a string slice with decimal digits.
1355+
///
1356+
/// The characters are expected to be an optional
1357+
#[doc = sign_dependent_expr!{
1358+
$signedness ?
1359+
if signed {
1360+
" `+` or `-` "
1361+
}
1362+
if unsigned {
1363+
" `+` "
1364+
}
1365+
}]
1366+
/// sign followed by only digits. Leading and trailing non-digit characters (including
1367+
/// whitespace) represent an error. Underscores (which are accepted in Rust literals)
1368+
/// also represent an error.
1369+
///
1370+
/// # Examples
1371+
///
1372+
/// Basic usage:
1373+
/// ```
1374+
/// use std::str::FromStr;
1375+
///
1376+
#[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_str(\"+10\"), Ok(10));")]
1377+
/// ```
1378+
/// Trailing space returns error:
1379+
/// ```
1380+
/// # use std::str::FromStr;
1381+
/// #
1382+
#[doc = concat!("assert!(", stringify!($int_ty), "::from_str(\"1 \").is_err());")]
1383+
/// ```
1384+
#[inline]
1385+
fn from_str(src: &str) -> Result<$int_ty, ParseIntError> {
1386+
<$int_ty>::from_str_radix(src, 10)
1387+
}
1388+
}
1389+
13641390
impl $int_ty {
1365-
/// Converts a string slice in a given base to an integer.
1391+
/// Parses an integer from a string slice with digits in a given base.
13661392
///
13671393
/// The string is expected to be an optional
13681394
#[doc = sign_dependent_expr!{
@@ -1375,7 +1401,7 @@ macro_rules! from_str_radix {
13751401
}
13761402
}]
13771403
/// sign followed by only digits. Leading and trailing non-digit characters (including
1378-
/// whitespace) represent an error. Underscores (which are accepted in rust literals)
1404+
/// whitespace) represent an error. Underscores (which are accepted in Rust literals)
13791405
/// also represent an error.
13801406
///
13811407
/// Digits are a subset of these characters, depending on `radix`:
@@ -1401,11 +1427,92 @@ macro_rules! from_str_radix {
14011427
#[rustc_const_stable(feature = "const_int_from_str", since = "1.82.0")]
14021428
#[inline]
14031429
pub const fn from_str_radix(src: &str, radix: u32) -> Result<$int_ty, ParseIntError> {
1430+
<$int_ty>::from_ascii_radix(src.as_bytes(), radix)
1431+
}
1432+
1433+
/// Parses an integer from an ASCII-byte slice with decimal digits.
1434+
///
1435+
/// The characters are expected to be an optional
1436+
#[doc = sign_dependent_expr!{
1437+
$signedness ?
1438+
if signed {
1439+
" `+` or `-` "
1440+
}
1441+
if unsigned {
1442+
" `+` "
1443+
}
1444+
}]
1445+
/// sign followed by only digits. Leading and trailing non-digit characters (including
1446+
/// whitespace) represent an error. Underscores (which are accepted in Rust literals)
1447+
/// also represent an error.
1448+
///
1449+
/// # Examples
1450+
///
1451+
/// Basic usage:
1452+
/// ```
1453+
/// #![feature(int_from_ascii)]
1454+
///
1455+
#[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_ascii(b\"+10\"), Ok(10));")]
1456+
/// ```
1457+
/// Trailing space returns error:
1458+
/// ```
1459+
/// # #![feature(int_from_ascii)]
1460+
/// #
1461+
#[doc = concat!("assert!(", stringify!($int_ty), "::from_ascii(b\"1 \").is_err());")]
1462+
/// ```
1463+
#[unstable(feature = "int_from_ascii", issue = "134821")]
1464+
#[inline]
1465+
pub const fn from_ascii(src: &[u8]) -> Result<$int_ty, ParseIntError> {
1466+
<$int_ty>::from_ascii_radix(src, 10)
1467+
}
1468+
1469+
/// Parses an integer from an ASCII-byte slice with digits in a given base.
1470+
///
1471+
/// The characters are expected to be an optional
1472+
#[doc = sign_dependent_expr!{
1473+
$signedness ?
1474+
if signed {
1475+
" `+` or `-` "
1476+
}
1477+
if unsigned {
1478+
" `+` "
1479+
}
1480+
}]
1481+
/// sign followed by only digits. Leading and trailing non-digit characters (including
1482+
/// whitespace) represent an error. Underscores (which are accepted in Rust literals)
1483+
/// also represent an error.
1484+
///
1485+
/// Digits are a subset of these characters, depending on `radix`:
1486+
/// * `0-9`
1487+
/// * `a-z`
1488+
/// * `A-Z`
1489+
///
1490+
/// # Panics
1491+
///
1492+
/// This function panics if `radix` is not in the range from 2 to 36.
1493+
///
1494+
/// # Examples
1495+
///
1496+
/// Basic usage:
1497+
/// ```
1498+
/// #![feature(int_from_ascii)]
1499+
///
1500+
#[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_ascii_radix(b\"A\", 16), Ok(10));")]
1501+
/// ```
1502+
/// Trailing space returns error:
1503+
/// ```
1504+
/// # #![feature(int_from_ascii)]
1505+
/// #
1506+
#[doc = concat!("assert!(", stringify!($int_ty), "::from_ascii_radix(b\"1 \", 10).is_err());")]
1507+
/// ```
1508+
#[unstable(feature = "int_from_ascii", issue = "134821")]
1509+
#[inline]
1510+
pub const fn from_ascii_radix(src: &[u8], radix: u32) -> Result<$int_ty, ParseIntError> {
14041511
use self::IntErrorKind::*;
14051512
use self::ParseIntError as PIE;
14061513

14071514
if 2 > radix || radix > 36 {
1408-
from_str_radix_panic(radix);
1515+
from_ascii_radix_panic(radix);
14091516
}
14101517

14111518
if src.is_empty() {
@@ -1415,12 +1522,6 @@ macro_rules! from_str_radix {
14151522
#[allow(unused_comparisons)]
14161523
let is_signed_ty = 0 > <$int_ty>::MIN;
14171524

1418-
// all valid digits are ascii, so we will just iterate over the utf8 bytes
1419-
// and cast them to chars. .to_digit() will safely return None for anything
1420-
// other than a valid ascii digit for the given radix, including the first-byte
1421-
// of multi-byte sequences
1422-
let src = src.as_bytes();
1423-
14241525
let (is_positive, mut digits) = match src {
14251526
[b'+' | b'-'] => {
14261527
return Err(PIE { kind: InvalidDigit });
@@ -1496,67 +1597,8 @@ macro_rules! from_str_radix {
14961597
Ok(result)
14971598
}
14981599
}
1499-
)+}
1500-
}
1501-
1502-
from_str_radix! { unsigned u8 u16 u32 u64 u128 }
1503-
from_str_radix! { signed i8 i16 i32 i64 i128 }
1504-
1505-
// Re-use the relevant implementation of from_str_radix for isize and usize to avoid outputting two
1506-
// identical functions.
1507-
macro_rules! from_str_radix_size_impl {
1508-
($($signedness:ident $t:ident $size:ty),*) => {$(
1509-
impl $size {
1510-
/// Converts a string slice in a given base to an integer.
1511-
///
1512-
/// The string is expected to be an optional
1513-
#[doc = sign_dependent_expr!{
1514-
$signedness ?
1515-
if signed {
1516-
" `+` or `-` "
1517-
}
1518-
if unsigned {
1519-
" `+` "
1520-
}
1521-
}]
1522-
/// sign followed by only digits. Leading and trailing non-digit characters (including
1523-
/// whitespace) represent an error. Underscores (which are accepted in rust literals)
1524-
/// also represent an error.
1525-
///
1526-
/// Digits are a subset of these characters, depending on `radix`:
1527-
/// * `0-9`
1528-
/// * `a-z`
1529-
/// * `A-Z`
1530-
///
1531-
/// # Panics
1532-
///
1533-
/// This function panics if `radix` is not in the range from 2 to 36.
1534-
///
1535-
/// # Examples
1536-
///
1537-
/// Basic usage:
1538-
/// ```
1539-
#[doc = concat!("assert_eq!(", stringify!($size), "::from_str_radix(\"A\", 16), Ok(10));")]
1540-
/// ```
1541-
/// Trailing space returns error:
1542-
/// ```
1543-
#[doc = concat!("assert!(", stringify!($size), "::from_str_radix(\"1 \", 10).is_err());")]
1544-
/// ```
1545-
#[stable(feature = "rust1", since = "1.0.0")]
1546-
#[rustc_const_stable(feature = "const_int_from_str", since = "1.82.0")]
1547-
#[inline]
1548-
pub const fn from_str_radix(src: &str, radix: u32) -> Result<$size, ParseIntError> {
1549-
match <$t>::from_str_radix(src, radix) {
1550-
Ok(x) => Ok(x as $size),
1551-
Err(e) => Err(e),
1552-
}
1553-
}
1554-
})*}
1600+
)*}
15551601
}
15561602

1557-
#[cfg(target_pointer_width = "16")]
1558-
from_str_radix_size_impl! { signed i16 isize, unsigned u16 usize }
1559-
#[cfg(target_pointer_width = "32")]
1560-
from_str_radix_size_impl! { signed i32 isize, unsigned u32 usize }
1561-
#[cfg(target_pointer_width = "64")]
1562-
from_str_radix_size_impl! { signed i64 isize, unsigned u64 usize }
1603+
from_str_int_impl! { signed isize i8 i16 i32 i64 i128 }
1604+
from_str_int_impl! { unsigned usize u8 u16 u32 u64 u128 }

‎tests/ui/consts/const-eval/parse_ints.stderr

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,34 @@
11
error[E0080]: evaluation of constant value failed
22
--> $SRC_DIR/core/src/num/mod.rs:LL:COL
33
|
4-
= note: the evaluated program panicked at 'from_str_radix_int: must lie in the range `[2, 36]`', $SRC_DIR/core/src/num/mod.rs:LL:COL
4+
= note: the evaluated program panicked at 'from_ascii_radix: radix must lie in the range `[2, 36]`', $SRC_DIR/core/src/num/mod.rs:LL:COL
55
|
6+
note: inside `core::num::<impl u64>::from_ascii_radix`
7+
--> $SRC_DIR/core/src/num/mod.rs:LL:COL
68
note: inside `core::num::<impl u64>::from_str_radix`
79
--> $SRC_DIR/core/src/num/mod.rs:LL:COL
810
note: inside `_TOO_LOW`
911
--> $DIR/parse_ints.rs:5:24
1012
|
1113
LL | const _TOO_LOW: () = { u64::from_str_radix("12345ABCD", 1); };
1214
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13-
= note: this error originates in the macro `from_str_radix` (in Nightly builds, run with -Z macro-backtrace for more info)
15+
= note: this error originates in the macro `from_str_int_impl` (in Nightly builds, run with -Z macro-backtrace for more info)
1416

1517
error[E0080]: evaluation of constant value failed
1618
--> $SRC_DIR/core/src/num/mod.rs:LL:COL
1719
|
18-
= note: the evaluated program panicked at 'from_str_radix_int: must lie in the range `[2, 36]`', $SRC_DIR/core/src/num/mod.rs:LL:COL
20+
= note: the evaluated program panicked at 'from_ascii_radix: radix must lie in the range `[2, 36]`', $SRC_DIR/core/src/num/mod.rs:LL:COL
1921
|
22+
note: inside `core::num::<impl u64>::from_ascii_radix`
23+
--> $SRC_DIR/core/src/num/mod.rs:LL:COL
2024
note: inside `core::num::<impl u64>::from_str_radix`
2125
--> $SRC_DIR/core/src/num/mod.rs:LL:COL
2226
note: inside `_TOO_HIGH`
2327
--> $DIR/parse_ints.rs:6:25
2428
|
2529
LL | const _TOO_HIGH: () = { u64::from_str_radix("12345ABCD", 37); };
2630
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
27-
= note: this error originates in the macro `from_str_radix` (in Nightly builds, run with -Z macro-backtrace for more info)
31+
= note: this error originates in the macro `from_str_int_impl` (in Nightly builds, run with -Z macro-backtrace for more info)
2832

2933
error: aborting due to 2 previous errors
3034

0 commit comments

Comments
 (0)
Failed to load comments.