about summary refs log tree commit diff
path: root/src/libcore
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2015-01-30 12:03:20 -0800
committerAlex Crichton <alex@alexcrichton.com>2015-01-30 12:03:20 -0800
commitac1a03d7422ba52749e4e513a46c8d2129c2c817 (patch)
treecefa26a551d7703c5f8534cc6661432348c93e06 /src/libcore
parent0ba812fbf00e3026b29282e1a72d58ea7959833e (diff)
parent0cdde6e5e015ee6f6d9381ab624a312af7c9b069 (diff)
downloadrust-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.rs174
-rw-r--r--src/libcore/option.rs4
-rw-r--r--src/libcore/str/mod.rs53
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