diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2015-01-30 12:03:20 -0800 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2015-01-30 12:03:20 -0800 |
| commit | ac1a03d7422ba52749e4e513a46c8d2129c2c817 (patch) | |
| tree | cefa26a551d7703c5f8534cc6661432348c93e06 /src/libcore | |
| parent | 0ba812fbf00e3026b29282e1a72d58ea7959833e (diff) | |
| parent | 0cdde6e5e015ee6f6d9381ab624a312af7c9b069 (diff) | |
| download | rust-ac1a03d7422ba52749e4e513a46c8d2129c2c817.tar.gz rust-ac1a03d7422ba52749e4e513a46c8d2129c2c817.zip | |
rollup merge of #21718: alexcrichton/stabilize-from-str
This commits adds an associated type to the `FromStr` trait representing an error payload for parses which do not succeed. The previous return value, `Option<Self>` did not allow for this form of payload. After the associated type was added, the following attributes were applied: * `FromStr` is now stable * `FromStr::Err` is now stable * `FromStr::from_str` is now stable * `StrExt::parse` is now stable * `FromStr for bool` is now stable * `FromStr for $float` is now stable * `FromStr for $integral` is now stable * Errors returned from stable `FromStr` implementations are stable * Errors implement `Display` and `Error` (both impl blocks being `#[stable]`) Closes #15138
Diffstat (limited to 'src/libcore')
| -rw-r--r-- | src/libcore/num/mod.rs | 174 | ||||
| -rw-r--r-- | src/libcore/option.rs | 4 | ||||
| -rw-r--r-- | src/libcore/str/mod.rs | 53 |
3 files changed, 164 insertions, 67 deletions
diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 1be7a0fb066..206e63c7a17 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -17,16 +17,17 @@ use char::CharExt; use clone::Clone; -use cmp::{PartialEq, Eq}; -use cmp::{PartialOrd, Ord}; +use cmp::{PartialEq, Eq, PartialOrd, Ord}; +use error::Error; +use fmt; use intrinsics; use iter::IteratorExt; use marker::Copy; use mem::size_of; use ops::{Add, Sub, Mul, Div, Rem, Neg}; use ops::{Not, BitAnd, BitOr, BitXor, Shl, Shr}; -use option::Option; -use option::Option::{Some, None}; +use option::Option::{self, Some, None}; +use result::Result::{self, Ok, Err}; use str::{FromStr, StrExt}; /// A built-in signed or unsigned integer. @@ -1428,22 +1429,25 @@ pub trait Float } /// A generic trait for converting a string with a radix (base) to a value -#[unstable(feature = "core", reason = "might need to return Result")] +#[unstable(feature = "core", reason = "needs reevaluation")] pub trait FromStrRadix { - fn from_str_radix(str: &str, radix: uint) -> Option<Self>; + type Err; + fn from_str_radix(str: &str, radix: uint) -> Result<Self, Self::Err>; } /// A utility function that just calls FromStrRadix::from_str_radix. -#[unstable(feature = "core", reason = "might need to return Result")] -pub fn from_str_radix<T: FromStrRadix>(str: &str, radix: uint) -> Option<T> { +#[unstable(feature = "core", reason = "needs reevaluation")] +pub fn from_str_radix<T: FromStrRadix>(str: &str, radix: uint) + -> Result<T, T::Err> { FromStrRadix::from_str_radix(str, radix) } macro_rules! from_str_radix_float_impl { ($T:ty) => { - #[unstable(feature = "core", - reason = "might need to return Result")] + #[stable(feature = "rust1", since = "1.0.0")] impl FromStr for $T { + type Err = ParseFloatError; + /// Convert a string in base 10 to a float. /// Accepts an optional decimal exponent. /// @@ -1470,14 +1474,15 @@ macro_rules! from_str_radix_float_impl { /// `None` if the string did not represent a valid number. Otherwise, /// `Some(n)` where `n` is the floating-point number represented by `src`. #[inline] - fn from_str(src: &str) -> Option<$T> { + fn from_str(src: &str) -> Result<$T, ParseFloatError> { from_str_radix(src, 10) } } - #[unstable(feature = "core", - reason = "might need to return Result")] + #[stable(feature = "rust1", since = "1.0.0")] impl FromStrRadix for $T { + type Err = ParseFloatError; + /// Convert a string in a given base to a float. /// /// Due to possible conflicts, this function does **not** accept @@ -1493,24 +1498,28 @@ macro_rules! from_str_radix_float_impl { /// /// # Return value /// - /// `None` if the string did not represent a valid number. Otherwise, - /// `Some(n)` where `n` is the floating-point number represented by `src`. - fn from_str_radix(src: &str, radix: uint) -> Option<$T> { - assert!(radix >= 2 && radix <= 36, + /// `None` if the string did not represent a valid number. + /// Otherwise, `Some(n)` where `n` is the floating-point number + /// represented by `src`. + fn from_str_radix(src: &str, radix: uint) + -> Result<$T, ParseFloatError> { + use self::FloatErrorKind::*; + use self::ParseFloatError as PFE; + assert!(radix >= 2 && radix <= 36, "from_str_radix_float: must lie in the range `[2, 36]` - found {}", radix); // Special values match src { - "inf" => return Some(Float::infinity()), - "-inf" => return Some(Float::neg_infinity()), - "NaN" => return Some(Float::nan()), + "inf" => return Ok(Float::infinity()), + "-inf" => return Ok(Float::neg_infinity()), + "NaN" => return Ok(Float::nan()), _ => {}, } let (is_positive, src) = match src.slice_shift_char() { - None => return None, - Some(('-', "")) => return None, + None => return Err(PFE { kind: Empty }), + Some(('-', "")) => return Err(PFE { kind: Empty }), Some(('-', src)) => (false, src), Some((_, _)) => (true, src), }; @@ -1541,15 +1550,15 @@ macro_rules! from_str_radix_float_impl { // if we've not seen any non-zero digits. if prev_sig != 0.0 { if is_positive && sig <= prev_sig - { return Some(Float::infinity()); } + { return Ok(Float::infinity()); } if !is_positive && sig >= prev_sig - { return Some(Float::neg_infinity()); } + { return Ok(Float::neg_infinity()); } // Detect overflow by reversing the shift-and-add process if is_positive && (prev_sig != (sig - digit as $T) / radix as $T) - { return Some(Float::infinity()); } + { return Ok(Float::infinity()); } if !is_positive && (prev_sig != (sig + digit as $T) / radix as $T) - { return Some(Float::neg_infinity()); } + { return Ok(Float::neg_infinity()); } } prev_sig = sig; }, @@ -1562,7 +1571,7 @@ macro_rules! from_str_radix_float_impl { break; // start of fractional part }, _ => { - return None; + return Err(PFE { kind: Invalid }); }, }, } @@ -1585,9 +1594,9 @@ macro_rules! from_str_radix_float_impl { }; // Detect overflow by comparing to last value if is_positive && sig < prev_sig - { return Some(Float::infinity()); } + { return Ok(Float::infinity()); } if !is_positive && sig > prev_sig - { return Some(Float::neg_infinity()); } + { return Ok(Float::neg_infinity()); } prev_sig = sig; }, None => match c { @@ -1596,7 +1605,7 @@ macro_rules! from_str_radix_float_impl { break; // start of exponent }, _ => { - return None; // invalid number + return Err(PFE { kind: Invalid }); }, }, } @@ -1609,7 +1618,7 @@ macro_rules! from_str_radix_float_impl { let base = match c { 'E' | 'e' if radix == 10 => 10.0, 'P' | 'p' if radix == 16 => 2.0, - _ => return None, + _ => return Err(PFE { kind: Invalid }), }; // Parse the exponent as decimal integer @@ -1618,19 +1627,19 @@ macro_rules! from_str_radix_float_impl { Some(('-', src)) => (false, src.parse::<uint>()), Some(('+', src)) => (true, src.parse::<uint>()), Some((_, _)) => (true, src.parse::<uint>()), - None => return None, + None => return Err(PFE { kind: Invalid }), }; match (is_positive, exp) { - (true, Some(exp)) => base.powi(exp as i32), - (false, Some(exp)) => 1.0 / base.powi(exp as i32), - (_, None) => return None, + (true, Ok(exp)) => base.powi(exp as i32), + (false, Ok(exp)) => 1.0 / base.powi(exp as i32), + (_, Err(_)) => return Err(PFE { kind: Invalid }), } }, None => 1.0, // no exponent }; - Some(sig * exp) + Ok(sig * exp) } } } @@ -1640,19 +1649,22 @@ from_str_radix_float_impl! { f64 } macro_rules! from_str_radix_int_impl { ($T:ty) => { - #[unstable(feature = "core", - reason = "might need to return Result")] + #[stable(feature = "rust1", since = "1.0.0")] impl FromStr for $T { + type Err = ParseIntError; #[inline] - fn from_str(src: &str) -> Option<$T> { + fn from_str(src: &str) -> Result<$T, ParseIntError> { from_str_radix(src, 10) } } - #[unstable(feature = "core", - reason = "might need to return Result")] + #[stable(feature = "rust1", since = "1.0.0")] impl FromStrRadix for $T { - fn from_str_radix(src: &str, radix: uint) -> Option<$T> { + type Err = ParseIntError; + fn from_str_radix(src: &str, radix: uint) + -> Result<$T, ParseIntError> { + use self::IntErrorKind::*; + use self::ParseIntError as PIE; assert!(radix >= 2 && radix <= 36, "from_str_radix_int: must lie in the range `[2, 36]` - found {}", radix); @@ -1666,18 +1678,18 @@ macro_rules! from_str_radix_int_impl { for c in src.chars() { let x = match c.to_digit(radix) { Some(x) => x, - None => return None, + None => return Err(PIE { kind: InvalidDigit }), }; result = match result.checked_mul(radix as $T) { Some(result) => result, - None => return None, + None => return Err(PIE { kind: Underflow }), }; result = match result.checked_sub(x as $T) { Some(result) => result, - None => return None, + None => return Err(PIE { kind: Underflow }), }; } - Some(result) + Ok(result) }, Some((_, _)) => { // The number is signed @@ -1685,20 +1697,20 @@ macro_rules! from_str_radix_int_impl { for c in src.chars() { let x = match c.to_digit(radix) { Some(x) => x, - None => return None, + None => return Err(PIE { kind: InvalidDigit }), }; result = match result.checked_mul(radix as $T) { Some(result) => result, - None => return None, + None => return Err(PIE { kind: Overflow }), }; result = match result.checked_add(x as $T) { Some(result) => result, - None => return None, + None => return Err(PIE { kind: Overflow }), }; } - Some(result) + Ok(result) }, - None => None, + None => Err(ParseIntError { kind: Empty }), } } } @@ -1714,3 +1726,63 @@ from_str_radix_int_impl! { u8 } from_str_radix_int_impl! { u16 } from_str_radix_int_impl! { u32 } from_str_radix_int_impl! { u64 } + +/// An error which can be returned when parsing an integer. +#[derive(Show, Clone, PartialEq)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct ParseIntError { kind: IntErrorKind } + +#[derive(Show, Clone, PartialEq)] +enum IntErrorKind { + Empty, + InvalidDigit, + Overflow, + Underflow, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for ParseIntError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.description().fmt(f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Error for ParseIntError { + fn description(&self) -> &str { + match self.kind { + IntErrorKind::Empty => "cannot parse integer from empty string", + IntErrorKind::InvalidDigit => "invalid digit found in string", + IntErrorKind::Overflow => "number too large to fit in target type", + IntErrorKind::Underflow => "number too small to fit in target type", + } + } +} + +/// An error which can be returned when parsing a float. +#[derive(Show, Clone, PartialEq)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct ParseFloatError { kind: FloatErrorKind } + +#[derive(Show, Clone, PartialEq)] +enum FloatErrorKind { + Empty, + Invalid, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for ParseFloatError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.description().fmt(f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Error for ParseFloatError { + fn description(&self) -> &str { + match self.kind { + FloatErrorKind::Empty => "cannot parse float from empty string", + FloatErrorKind::Invalid => "invalid float literal", + } + } +} diff --git a/src/libcore/option.rs b/src/libcore/option.rs index 5cb8e5e5565..2f261b0628f 100644 --- a/src/libcore/option.rs +++ b/src/libcore/option.rs @@ -728,8 +728,8 @@ impl<T: Default> Option<T> { /// ``` /// let good_year_from_input = "1909"; /// let bad_year_from_input = "190blarg"; - /// let good_year = good_year_from_input.parse().unwrap_or_default(); - /// let bad_year = bad_year_from_input.parse().unwrap_or_default(); + /// let good_year = good_year_from_input.parse().ok().unwrap_or_default(); + /// let bad_year = bad_year_from_input.parse().ok().unwrap_or_default(); /// /// assert_eq!(1909, good_year); /// assert_eq!(0, bad_year); diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index f545c56a060..93942d0b836 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -109,37 +109,62 @@ macro_rules! delegate_iter { /// A trait to abstract the idea of creating a new instance of a type from a /// string. -// FIXME(#17307): there should be an `E` associated type for a `Result` return -#[unstable(feature = "core", - reason = "will return a Result once associated types are working")] +#[stable(feature = "rust1", since = "1.0.0")] pub trait FromStr { + /// The associated error which can be returned from parsing. + #[stable(feature = "rust1", since = "1.0.0")] + type Err; + /// Parses a string `s` to return an optional value of this type. If the /// string is ill-formatted, the None is returned. - fn from_str(s: &str) -> Option<Self>; + #[stable(feature = "rust1", since = "1.0.0")] + fn from_str(s: &str) -> Result<Self, Self::Err>; } +#[stable(feature = "rust1", since = "1.0.0")] impl FromStr for bool { + type Err = ParseBoolError; + /// Parse a `bool` from a string. /// - /// Yields an `Option<bool>`, because `s` may or may not actually be parseable. + /// Yields an `Option<bool>`, because `s` may or may not actually be + /// parseable. /// /// # Examples /// /// ```rust - /// assert_eq!("true".parse(), Some(true)); - /// assert_eq!("false".parse(), Some(false)); - /// assert_eq!("not even a boolean".parse::<bool>(), None); + /// assert_eq!("true".parse(), Ok(true)); + /// assert_eq!("false".parse(), Ok(false)); + /// assert!("not even a boolean".parse::<bool>().is_err()); /// ``` #[inline] - fn from_str(s: &str) -> Option<bool> { + fn from_str(s: &str) -> Result<bool, ParseBoolError> { match s { - "true" => Some(true), - "false" => Some(false), - _ => None, + "true" => Ok(true), + "false" => Ok(false), + _ => Err(ParseBoolError { _priv: () }), } } } +/// An error returned when parsing a `bool` from a string fails. +#[derive(Show, Clone, PartialEq)] +#[allow(missing_copy_implementations)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct ParseBoolError { _priv: () } + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for ParseBoolError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + "provided string was not `true` or `false`".fmt(f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Error for ParseBoolError { + fn description(&self) -> &str { "failed to parse bool" } +} + /* Section: Creating a string */ @@ -1356,7 +1381,7 @@ pub trait StrExt { fn as_ptr(&self) -> *const u8; fn len(&self) -> uint; fn is_empty(&self) -> bool; - fn parse<T: FromStr>(&self) -> Option<T>; + fn parse<T: FromStr>(&self) -> Result<T, T::Err>; } #[inline(never)] @@ -1671,7 +1696,7 @@ impl StrExt for str { fn is_empty(&self) -> bool { self.len() == 0 } #[inline] - fn parse<T: FromStr>(&self) -> Option<T> { FromStr::from_str(self) } + fn parse<T: FromStr>(&self) -> Result<T, T::Err> { FromStr::from_str(self) } } /// Pluck a code point out of a UTF-8-like byte slice and return the |
