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
This commit is contained in:
Alex Crichton 2015-01-30 12:03:20 -08:00
commit ac1a03d742
39 changed files with 389 additions and 260 deletions

View file

@ -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",
}
}
}

View file

@ -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);

View file

@ -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