Skip to content

Commit 7d0518c

Browse files
committed
Implement int_from_ascii (rust-lang#134821)
Provides unstable `T::from_ascii()` and `T::from_ascii_radix()` for integer types `T`, as drafted in tracking issue rust-lang#134821. To deduplicate documentation without additional macros, implementations of `isize` and `usize` no longer delegate to equivalent integer types. After rust-lang#132870 they are inlined anyway.
1 parent 6d3db55 commit 7d0518c

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
@@ -1453,20 +1453,6 @@ pub enum FpCategory {
14531453
Normal,
14541454
}
14551455

1456-
macro_rules! from_str_radix_int_impl {
1457-
($($t:ty)*) => {$(
1458-
#[stable(feature = "rust1", since = "1.0.0")]
1459-
impl FromStr for $t {
1460-
type Err = ParseIntError;
1461-
#[inline]
1462-
fn from_str(src: &str) -> Result<Self, ParseIntError> {
1463-
<$t>::from_str_radix(src, 10)
1464-
}
1465-
}
1466-
)*}
1467-
}
1468-
from_str_radix_int_impl! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 }
1469-
14701456
/// Determines if a string of text of that length of that radix could be guaranteed to be
14711457
/// stored in the given type T.
14721458
/// Note that if the radix is known to the compiler, it is just the check of digits.len that
@@ -1482,18 +1468,58 @@ pub const fn can_not_overflow<T>(radix: u32, is_signed_ty: bool, digits: &[u8])
14821468
#[cfg_attr(feature = "panic_immediate_abort", inline)]
14831469
#[cold]
14841470
#[track_caller]
1485-
const fn from_str_radix_panic(radix: u32) -> ! {
1471+
const fn from_ascii_radix_panic(radix: u32) -> ! {
14861472
const_panic!(
1487-
"from_str_radix_int: must lie in the range `[2, 36]`",
1488-
"from_str_radix_int: must lie in the range `[2, 36]` - found {radix}",
1473+
"from_ascii_radix: radix must lie in the range `[2, 36]`",
1474+
"from_ascii_radix: radix must lie in the range `[2, 36]` - found {radix}",
14891475
radix: u32 = radix,
14901476
)
14911477
}
14921478

1493-
macro_rules! from_str_radix {
1479+
macro_rules! from_str_int_impl {
14941480
($signedness:ident $($int_ty:ty)+) => {$(
1481+
#[stable(feature = "rust1", since = "1.0.0")]
1482+
impl FromStr for $int_ty {
1483+
type Err = ParseIntError;
1484+
1485+
/// Parses an integer from a string slice with decimal digits.
1486+
///
1487+
/// The characters are expected to be an optional
1488+
#[doc = sign_dependent_expr!{
1489+
$signedness ?
1490+
if signed {
1491+
" `+` or `-` "
1492+
}
1493+
if unsigned {
1494+
" `+` "
1495+
}
1496+
}]
1497+
/// sign followed by only digits. Leading and trailing non-digit characters (including
1498+
/// whitespace) represent an error. Underscores (which are accepted in Rust literals)
1499+
/// also represent an error.
1500+
///
1501+
/// # Examples
1502+
///
1503+
/// Basic usage:
1504+
/// ```
1505+
/// use std::str::FromStr;
1506+
///
1507+
#[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_str(\"+10\"), Ok(10));")]
1508+
/// ```
1509+
/// Trailing space returns error:
1510+
/// ```
1511+
/// # use std::str::FromStr;
1512+
/// #
1513+
#[doc = concat!("assert!(", stringify!($int_ty), "::from_str(\"1 \").is_err());")]
1514+
/// ```
1515+
#[inline]
1516+
fn from_str(src: &str) -> Result<$int_ty, ParseIntError> {
1517+
<$int_ty>::from_str_radix(src, 10)
1518+
}
1519+
}
1520+
14951521
impl $int_ty {
1496-
/// Converts a string slice in a given base to an integer.
1522+
/// Parses an integer from a string slice with digits in a given base.
14971523
///
14981524
/// The string is expected to be an optional
14991525
#[doc = sign_dependent_expr!{
@@ -1506,7 +1532,7 @@ macro_rules! from_str_radix {
15061532
}
15071533
}]
15081534
/// sign followed by only digits. Leading and trailing non-digit characters (including
1509-
/// whitespace) represent an error. Underscores (which are accepted in rust literals)
1535+
/// whitespace) represent an error. Underscores (which are accepted in Rust literals)
15101536
/// also represent an error.
15111537
///
15121538
/// Digits are a subset of these characters, depending on `radix`:
@@ -1532,11 +1558,92 @@ macro_rules! from_str_radix {
15321558
#[rustc_const_stable(feature = "const_int_from_str", since = "1.82.0")]
15331559
#[inline]
15341560
pub const fn from_str_radix(src: &str, radix: u32) -> Result<$int_ty, ParseIntError> {
1561+
<$int_ty>::from_ascii_radix(src.as_bytes(), radix)
1562+
}
1563+
1564+
/// Parses an integer from an ASCII-byte slice with decimal digits.
1565+
///
1566+
/// The characters are expected to be an optional
1567+
#[doc = sign_dependent_expr!{
1568+
$signedness ?
1569+
if signed {
1570+
" `+` or `-` "
1571+
}
1572+
if unsigned {
1573+
" `+` "
1574+
}
1575+
}]
1576+
/// sign followed by only digits. Leading and trailing non-digit characters (including
1577+
/// whitespace) represent an error. Underscores (which are accepted in Rust literals)
1578+
/// also represent an error.
1579+
///
1580+
/// # Examples
1581+
///
1582+
/// Basic usage:
1583+
/// ```
1584+
/// #![feature(int_from_ascii)]
1585+
///
1586+
#[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_ascii(b\"+10\"), Ok(10));")]
1587+
/// ```
1588+
/// Trailing space returns error:
1589+
/// ```
1590+
/// # #![feature(int_from_ascii)]
1591+
/// #
1592+
#[doc = concat!("assert!(", stringify!($int_ty), "::from_ascii(b\"1 \").is_err());")]
1593+
/// ```
1594+
#[unstable(feature = "int_from_ascii", issue = "134821")]
1595+
#[inline]
1596+
pub const fn from_ascii(src: &[u8]) -> Result<$int_ty, ParseIntError> {
1597+
<$int_ty>::from_ascii_radix(src, 10)
1598+
}
1599+
1600+
/// Parses an integer from an ASCII-byte slice with digits in a given base.
1601+
///
1602+
/// The characters are expected to be an optional
1603+
#[doc = sign_dependent_expr!{
1604+
$signedness ?
1605+
if signed {
1606+
" `+` or `-` "
1607+
}
1608+
if unsigned {
1609+
" `+` "
1610+
}
1611+
}]
1612+
/// sign followed by only digits. Leading and trailing non-digit characters (including
1613+
/// whitespace) represent an error. Underscores (which are accepted in Rust literals)
1614+
/// also represent an error.
1615+
///
1616+
/// Digits are a subset of these characters, depending on `radix`:
1617+
/// * `0-9`
1618+
/// * `a-z`
1619+
/// * `A-Z`
1620+
///
1621+
/// # Panics
1622+
///
1623+
/// This function panics if `radix` is not in the range from 2 to 36.
1624+
///
1625+
/// # Examples
1626+
///
1627+
/// Basic usage:
1628+
/// ```
1629+
/// #![feature(int_from_ascii)]
1630+
///
1631+
#[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_ascii_radix(b\"A\", 16), Ok(10));")]
1632+
/// ```
1633+
/// Trailing space returns error:
1634+
/// ```
1635+
/// # #![feature(int_from_ascii)]
1636+
/// #
1637+
#[doc = concat!("assert!(", stringify!($int_ty), "::from_ascii_radix(b\"1 \", 10).is_err());")]
1638+
/// ```
1639+
#[unstable(feature = "int_from_ascii", issue = "134821")]
1640+
#[inline]
1641+
pub const fn from_ascii_radix(src: &[u8], radix: u32) -> Result<$int_ty, ParseIntError> {
15351642
use self::IntErrorKind::*;
15361643
use self::ParseIntError as PIE;
15371644

15381645
if 2 > radix || radix > 36 {
1539-
from_str_radix_panic(radix);
1646+
from_ascii_radix_panic(radix);
15401647
}
15411648

15421649
if src.is_empty() {
@@ -1546,12 +1653,6 @@ macro_rules! from_str_radix {
15461653
#[allow(unused_comparisons)]
15471654
let is_signed_ty = 0 > <$int_ty>::MIN;
15481655

1549-
// all valid digits are ascii, so we will just iterate over the utf8 bytes
1550-
// and cast them to chars. .to_digit() will safely return None for anything
1551-
// other than a valid ascii digit for the given radix, including the first-byte
1552-
// of multi-byte sequences
1553-
let src = src.as_bytes();
1554-
15551656
let (is_positive, mut digits) = match src {
15561657
[b'+' | b'-'] => {
15571658
return Err(PIE { kind: InvalidDigit });
@@ -1627,67 +1728,8 @@ macro_rules! from_str_radix {
16271728
Ok(result)
16281729
}
16291730
}
1630-
)+}
1631-
}
1632-
1633-
from_str_radix! { unsigned u8 u16 u32 u64 u128 }
1634-
from_str_radix! { signed i8 i16 i32 i64 i128 }
1635-
1636-
// Re-use the relevant implementation of from_str_radix for isize and usize to avoid outputting two
1637-
// identical functions.
1638-
macro_rules! from_str_radix_size_impl {
1639-
($($signedness:ident $t:ident $size:ty),*) => {$(
1640-
impl $size {
1641-
/// Converts a string slice in a given base to an integer.
1642-
///
1643-
/// The string is expected to be an optional
1644-
#[doc = sign_dependent_expr!{
1645-
$signedness ?
1646-
if signed {
1647-
" `+` or `-` "
1648-
}
1649-
if unsigned {
1650-
" `+` "
1651-
}
1652-
}]
1653-
/// sign followed by only digits. Leading and trailing non-digit characters (including
1654-
/// whitespace) represent an error. Underscores (which are accepted in rust literals)
1655-
/// also represent an error.
1656-
///
1657-
/// Digits are a subset of these characters, depending on `radix`:
1658-
/// * `0-9`
1659-
/// * `a-z`
1660-
/// * `A-Z`
1661-
///
1662-
/// # Panics
1663-
///
1664-
/// This function panics if `radix` is not in the range from 2 to 36.
1665-
///
1666-
/// # Examples
1667-
///
1668-
/// Basic usage:
1669-
/// ```
1670-
#[doc = concat!("assert_eq!(", stringify!($size), "::from_str_radix(\"A\", 16), Ok(10));")]
1671-
/// ```
1672-
/// Trailing space returns error:
1673-
/// ```
1674-
#[doc = concat!("assert!(", stringify!($size), "::from_str_radix(\"1 \", 10).is_err());")]
1675-
/// ```
1676-
#[stable(feature = "rust1", since = "1.0.0")]
1677-
#[rustc_const_stable(feature = "const_int_from_str", since = "1.82.0")]
1678-
#[inline]
1679-
pub const fn from_str_radix(src: &str, radix: u32) -> Result<$size, ParseIntError> {
1680-
match <$t>::from_str_radix(src, radix) {
1681-
Ok(x) => Ok(x as $size),
1682-
Err(e) => Err(e),
1683-
}
1684-
}
1685-
})*}
1731+
)*}
16861732
}
16871733

1688-
#[cfg(target_pointer_width = "16")]
1689-
from_str_radix_size_impl! { signed i16 isize, unsigned u16 usize }
1690-
#[cfg(target_pointer_width = "32")]
1691-
from_str_radix_size_impl! { signed i32 isize, unsigned u32 usize }
1692-
#[cfg(target_pointer_width = "64")]
1693-
from_str_radix_size_impl! { signed i64 isize, unsigned u64 usize }
1734+
from_str_int_impl! { signed isize i8 i16 i32 i64 i128 }
1735+
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)