Skip to content

Implement TryFrom<&str> for numerical types, bool and char #104283

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions library/core/src/char/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,20 @@ impl FromStr for char {
}
}

impl<'a> TryFrom<&'a str> for char {
type Error = ParseCharError;

#[inline]
fn try_from(s: &'a str) -> Result<Self, Self::Err> {
let mut chars = s.chars();
match (chars.next(), chars.next()) {
(None, _) => Err(ParseCharError { kind: CharErrorKind::EmptyString }),
(Some(c), None) => Ok(c),
_ => Err(ParseCharError { kind: CharErrorKind::TooManyChars }),
}
}
}

#[inline]
const fn char_try_from_u32(i: u32) -> Result<char, CharTryFromError> {
// This is an optimized version of the check
Expand Down
61 changes: 61 additions & 0 deletions library/core/src/num/dec2flt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
issue = "none"
)]

use crate::convert::TryFrom;
use crate::fmt;
use crate::str::FromStr;

Expand Down Expand Up @@ -156,6 +157,66 @@ macro_rules! from_str_float_impl {
from_str_float_impl!(f32);
from_str_float_impl!(f64);

macro_rules! try_from_str_float_impl {
($t:ty) => {
impl<'a> TryFrom<&'a str> for $t {
type Error = ParseFloatError;

/// Converts a string in base 10 to a float.
/// Accepts an optional decimal exponent.
///
/// This function accepts strings such as
///
/// * '3.14'
/// * '-3.14'
/// * '2.5E10', or equivalently, '2.5e10'
/// * '2.5E-10'
/// * '5.'
/// * '.5', or, equivalently, '0.5'
/// * 'inf', '-inf', '+infinity', 'NaN'
///
/// Note that alphabetical characters are not case-sensitive.
///
/// Leading and trailing whitespace represent an error.
///
/// # Grammar
///
/// All strings that adhere to the following [EBNF] grammar when
/// lowercased will result in an [`Ok`] being returned:
///
/// ```txt
/// Float ::= Sign? ( 'inf' | 'infinity' | 'nan' | Number )
/// Number ::= ( Digit+ |
/// Digit+ '.' Digit* |
/// Digit* '.' Digit+ ) Exp?
/// Exp ::= 'e' Sign? Digit+
/// Sign ::= [+-]
/// Digit ::= [0-9]
/// ```
///
/// [EBNF]: https://www.w3.org/TR/REC-xml/#sec-notation
///
/// # Arguments
///
/// * src - A string
///
/// # Return value
///
/// `Err(ParseFloatError)` if the string did not represent a valid
/// number. Otherwise, `Ok(n)` where `n` is the closest
/// representable floating-point number to the number represented
/// by `src` (following the same rules for rounding as for the
/// results of primitive operations).
#[inline]
fn try_from(src: &'a str) -> Result<Self, ParseFloatError> {
dec2flt(src)
}
}
};
}
try_from_str_float_impl!(f32);
try_from_str_float_impl!(f64);

/// An error which can be returned when parsing a float.
///
/// This error is used as the error type for the [`FromStr`] implementation
Expand Down
13 changes: 13 additions & 0 deletions library/core/src/num/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#![stable(feature = "rust1", since = "1.0.0")]

use crate::ascii;
use crate::convert::TryFrom;
use crate::error::Error;
use crate::intrinsics;
use crate::mem;
Expand Down Expand Up @@ -1077,6 +1078,18 @@ macro_rules! from_str_radix_int_impl {
}
from_str_radix_int_impl! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 }

macro_rules! try_from_str_radix_int_impl {
($($t:ty)*) => {$(
impl<'a> TryFrom<&'a str> for $t {
type Error = ParseIntError;
fn try_from(src: &'a str) -> Result<Self, ParseIntError> {
from_str_radix(src, 10)
}
}
)*}
}
try_from_str_radix_int_impl! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 }

macro_rules! impl_helper_for {
($($t:ty)*) => ($(impl FromStrRadixHelper for $t {
const MIN: Self = Self::MIN;
Expand Down
18 changes: 18 additions & 0 deletions library/core/src/num/nonzero.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Definitions of integer that is known not to equal zero.

use crate::convert::TryFrom;
use crate::fmt;
use crate::ops::{BitOr, BitOrAssign, Div, Rem};
use crate::str::FromStr;
Expand Down Expand Up @@ -194,6 +195,23 @@ macro_rules! from_str_radix_nzint_impl {
from_str_radix_nzint_impl! { NonZeroU8 NonZeroU16 NonZeroU32 NonZeroU64 NonZeroU128 NonZeroUsize
NonZeroI8 NonZeroI16 NonZeroI32 NonZeroI64 NonZeroI128 NonZeroIsize }

macro_rules! try_from_str_radix_nzint_impl {
($($t:ty)*) => {$(
impl<'a> TryFrom<&'a str> for $t {
type Error = ParseIntError;
fn try_from(src: &'a str) -> Result<Self, Self::Err> {
Self::new(from_str_radix(src, 10)?)
.ok_or(ParseIntError {
kind: IntErrorKind::Zero
})
}
}
)*}
}

try_from_str_radix_nzint_impl! { NonZeroU8 NonZeroU16 NonZeroU32 NonZeroU64 NonZeroU128 NonZeroUsize
NonZeroI8 NonZeroI16 NonZeroI32 NonZeroI64 NonZeroI128 NonZeroIsize }

macro_rules! nonzero_leading_trailing_zeros {
( $( $Ty: ident($Uint: ty) , $LeadingTestExpr:expr ;)+ ) => {
$(
Expand Down
60 changes: 56 additions & 4 deletions library/core/src/str/traits.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Trait implementations for `str`.

use crate::cmp::Ordering;
use crate::convert::TryFrom;
use crate::ops;
use crate::ptr;
use crate::slice::SliceIndex;
Expand Down Expand Up @@ -404,11 +405,19 @@ unsafe impl const SliceIndex<str> for ops::RangeInclusive<usize> {
type Output = str;
#[inline]
fn get(self, slice: &str) -> Option<&Self::Output> {
if *self.end() == usize::MAX { None } else { self.into_slice_range().get(slice) }
if *self.end() == usize::MAX {
None
} else {
self.into_slice_range().get(slice)
}
}
#[inline]
fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> {
if *self.end() == usize::MAX { None } else { self.into_slice_range().get_mut(slice) }
if *self.end() == usize::MAX {
None
} else {
self.into_slice_range().get_mut(slice)
}
}
#[inline]
unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output {
Expand Down Expand Up @@ -456,11 +465,19 @@ unsafe impl const SliceIndex<str> for ops::RangeToInclusive<usize> {
type Output = str;
#[inline]
fn get(self, slice: &str) -> Option<&Self::Output> {
if self.end == usize::MAX { None } else { (..self.end + 1).get(slice) }
if self.end == usize::MAX {
None
} else {
(..self.end + 1).get(slice)
}
}
#[inline]
fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> {
if self.end == usize::MAX { None } else { (..self.end + 1).get_mut(slice) }
if self.end == usize::MAX {
None
} else {
(..self.end + 1).get_mut(slice)
}
}
#[inline]
unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output {
Expand Down Expand Up @@ -606,3 +623,38 @@ impl FromStr for bool {
}
}
}

impl<'a> TryFrom<&'a str> for bool {
type Error = ParseBoolError;

/// Parse a `bool` from a string.
///
/// The only accepted values are `"true"` and `"false"`. Any other input
/// will return an error.
///
/// # Examples
///
/// ```
/// use std::str::FromStr;
///
/// assert_eq!(FromStr::from_str("true"), Ok(true));
/// assert_eq!(FromStr::from_str("false"), Ok(false));
/// assert!(<bool as FromStr>::from_str("not even a boolean").is_err());
/// ```
///
/// Note, in many cases, the `.parse()` method on `str` is more proper.
///
/// ```
/// assert_eq!("true".parse(), Ok(true));
/// assert_eq!("false".parse(), Ok(false));
/// assert!("not even a boolean".parse::<bool>().is_err());
/// ```
#[inline]
fn try_from(s: &'a str) -> Result<bool, ParseBoolError> {
match s {
"true" => Ok(true),
"false" => Ok(false),
_ => Err(ParseBoolError),
}
}
}