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:
commit
ac1a03d742
39 changed files with 389 additions and 260 deletions
|
|
@ -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",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue