From 36a77f4cef5d4d1cc347144bd579bd5a565d2b6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maro=C5=A1=20Grego?= Date: Fri, 11 Nov 2022 14:34:31 +0100 Subject: [PATCH 1/2] Implement TryFrom<&str> for numerical types, bool and char --- library/core/src/char/convert.rs | 14 +++++++ library/core/src/num/dec2flt/mod.rs | 61 +++++++++++++++++++++++++++++ library/core/src/num/mod.rs | 13 ++++++ library/core/src/num/nonzero.rs | 17 ++++++++ library/core/src/str/traits.rs | 60 ++++++++++++++++++++++++++-- 5 files changed, 161 insertions(+), 4 deletions(-) diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs index 7c5f82f5ea49d..e5ce5a8e3c6c3 100644 --- a/library/core/src/char/convert.rs +++ b/library/core/src/char/convert.rs @@ -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 { + 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 { // This is an optimized version of the check diff --git a/library/core/src/num/dec2flt/mod.rs b/library/core/src/num/dec2flt/mod.rs index a888ced49b376..bb7d7277d9515 100644 --- a/library/core/src/num/dec2flt/mod.rs +++ b/library/core/src/num/dec2flt/mod.rs @@ -75,6 +75,7 @@ issue = "none" )] +use crate::convert::TryFrom; use crate::fmt; use crate::str::FromStr; @@ -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 { + 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 diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 311c5fa5b6834..14acf65126e04 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -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; @@ -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 { + 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; diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 5b7521220acdb..2ea583ef7fec3 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -194,6 +194,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::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 ;)+ ) => { $( diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs index d3ed811b1575b..f8fe263cbb876 100644 --- a/library/core/src/str/traits.rs +++ b/library/core/src/str/traits.rs @@ -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; @@ -404,11 +405,19 @@ unsafe impl const SliceIndex for ops::RangeInclusive { 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 { @@ -456,11 +465,19 @@ unsafe impl const SliceIndex for ops::RangeToInclusive { 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 { @@ -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!(::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::().is_err()); + /// ``` + #[inline] + fn try_from(s: &'a str) -> Result { + match s { + "true" => Ok(true), + "false" => Ok(false), + _ => Err(ParseBoolError), + } + } +} From 8e23201d8486bcc562bd63858e6fe8b81a94ddf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maro=C5=A1=20Grego?= Date: Fri, 11 Nov 2022 20:43:38 +0100 Subject: [PATCH 2/2] Use TryFrom in nonzero.rs --- library/core/src/num/nonzero.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 2ea583ef7fec3..e84b3c2be00df 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -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;