Skip to content

Commit

Permalink
Implement RFC 1542
Browse files Browse the repository at this point in the history
  • Loading branch information
sfackler committed May 7, 2016
1 parent 936b32a commit a9779df
Show file tree
Hide file tree
Showing 7 changed files with 451 additions and 159 deletions.
57 changes: 47 additions & 10 deletions src/libcore/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,19 @@
//! - Impl the `As*` traits for reference-to-reference conversions
//! - Impl the `Into` trait when you want to consume the value in the conversion
//! - The `From` trait is the most flexible, useful for value _and_ reference conversions
//! - The `TryFrom` and `TryInto` traits behave like `From` and `Into`, but allow for the
//! conversion to fail
//!
//! As a library author, you should prefer implementing `From<T>` rather than
//! `Into<U>`, as `From` provides greater flexibility and offers an equivalent `Into`
//! implementation for free, thanks to a blanket implementation in the standard library.
//!
//! **Note: these traits must not fail**. If the conversion can fail, you must use a dedicated
//! method which returns an `Option<T>` or a `Result<T, E>`.
//! As a library author, you should prefer implementing `From<T>` or `TryFrom<T>` rather than
//! `Into<U>` or `TryInto<U>`, as `From` and `TryFrom` provide greater flexibility and offer
//! equivalent `Into` or `TryInto` implementations for free, thanks to a blanket implementation
//! in the standard library.
//!
//! # Generic impl
//!
//! - `AsRef` and `AsMut` auto-dereference if the inner type is a reference
//! - `From<U> for T` implies `Into<T> for U`
//! - `TryFrom<U> for T` implies `TryInto<T> for U`
//! - `From` and `Into` are reflexive, which means that all types can `into()`
//! themselves and `from()` themselves
//!
Expand All @@ -40,6 +41,7 @@
#![stable(feature = "rust1", since = "1.0.0")]

use marker::Sized;
use result::Result;

/// A cheap, reference-to-reference conversion.
///
Expand Down Expand Up @@ -98,8 +100,8 @@ pub trait AsMut<T: ?Sized> {

/// A conversion that consumes `self`, which may or may not be expensive.
///
/// **Note: this trait must not fail**. If the conversion can fail, use a dedicated method which
/// returns an `Option<T>` or a `Result<T, E>`.
/// **Note: this trait must not fail**. If the conversion can fail, use `TryInto` or a dedicated
/// method which returns an `Option<T>` or a `Result<T, E>`.
///
/// Library authors should not directly implement this trait, but should prefer implementing
/// the `From` trait, which offers greater flexibility and provides an equivalent `Into`
Expand Down Expand Up @@ -133,8 +135,8 @@ pub trait Into<T>: Sized {

/// Construct `Self` via a conversion.
///
/// **Note: this trait must not fail**. If the conversion can fail, use a dedicated method which
/// returns an `Option<T>` or a `Result<T, E>`.
/// **Note: this trait must not fail**. If the conversion can fail, use `TryFrom` or a dedicated
/// method which returns an `Option<T>` or a `Result<T, E>`.
///
/// # Examples
///
Expand All @@ -158,6 +160,30 @@ pub trait From<T>: Sized {
fn from(T) -> Self;
}

/// An attempted conversion that consumes `self`, which may or may not be expensive.
///
/// Library authors should not directly implement this trait, but should prefer implementing
/// the `TryFrom` trait, which offers greater flexibility and provides an equivalent `TryInto`
/// implementation for free, thanks to a blanket implementation in the standard library.
#[unstable(feature = "try_from", issue = "33417")]
pub trait TryInto<T>: Sized {
/// The type returned in the event of a conversion error.
type Err;

/// Performs the conversion.
fn try_into(self) -> Result<T, Self::Err>;
}

/// Attempt to construct `Self` via a conversion.
#[unstable(feature = "try_from", issue = "33417")]
pub trait TryFrom<T>: Sized {
/// The type returned in the event of a conversion error.
type Err;

/// Performs the conversion.
fn try_from(T) -> Result<Self, Self::Err>;
}

////////////////////////////////////////////////////////////////////////////////
// GENERIC IMPLS
////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -216,6 +242,17 @@ impl<T> From<T> for T {
fn from(t: T) -> T { t }
}


// TryFrom implies TryInto
#[unstable(feature = "try_from", issue = "33417")]
impl<T, U> TryInto<U> for T where U: TryFrom<T> {
type Err = U::Err;

fn try_into(self) -> Result<U, U::Err> {
U::try_from(self)
}
}

////////////////////////////////////////////////////////////////////////////////
// CONCRETE IMPLS
////////////////////////////////////////////////////////////////////////////////
Expand Down
95 changes: 94 additions & 1 deletion src/libcore/num/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

use char::CharExt;
use cmp::PartialOrd;
use convert::From;
use convert::{From, TryFrom};
use fmt;
use intrinsics;
use marker::{Copy, Sized};
Expand Down Expand Up @@ -2341,9 +2341,101 @@ macro_rules! from_str_radix_int_impl {
}
from_str_radix_int_impl! { isize i8 i16 i32 i64 usize u8 u16 u32 u64 }

/// The error type returned when a checked integral type conversion fails.
#[unstable(feature = "try_from", issue = "33417")]
#[derive(Debug, Copy, Clone)]
pub struct TryFromIntError(());

impl TryFromIntError {
#[unstable(feature = "int_error_internals",
reason = "available through Error trait and this method should \
not be exposed publicly",
issue = "0")]
#[doc(hidden)]
pub fn __description(&self) -> &str {
"out of range integral type conversion attempted"
}
}

#[unstable(feature = "try_from", issue = "33417")]
impl fmt::Display for TryFromIntError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
self.__description().fmt(fmt)
}
}

macro_rules! same_sign_from_int_impl {
($storage:ty, $target:ty, $($source:ty),*) => {$(
#[stable(feature = "rust1", since = "1.0.0")]
impl TryFrom<$source> for $target {
type Err = TryFromIntError;

fn try_from(u: $source) -> Result<$target, TryFromIntError> {
let min = <$target as FromStrRadixHelper>::min_value() as $storage;
let max = <$target as FromStrRadixHelper>::max_value() as $storage;
if u as $storage < min || u as $storage > max {
Err(TryFromIntError(()))
} else {
Ok(u as $target)
}
}
}
)*}
}

same_sign_from_int_impl!(u64, u8, u8, u16, u32, u64, usize);
same_sign_from_int_impl!(i64, i8, i8, i16, i32, i64, isize);
same_sign_from_int_impl!(u64, u16, u8, u16, u32, u64, usize);
same_sign_from_int_impl!(i64, i16, i8, i16, i32, i64, isize);
same_sign_from_int_impl!(u64, u32, u8, u16, u32, u64, usize);
same_sign_from_int_impl!(i64, i32, i8, i16, i32, i64, isize);
same_sign_from_int_impl!(u64, u64, u8, u16, u32, u64, usize);
same_sign_from_int_impl!(i64, i64, i8, i16, i32, i64, isize);
same_sign_from_int_impl!(u64, usize, u8, u16, u32, u64, usize);
same_sign_from_int_impl!(i64, isize, i8, i16, i32, i64, isize);

macro_rules! cross_sign_from_int_impl {
($unsigned:ty, $($signed:ty),*) => {$(
#[stable(feature = "rust1", since = "1.0.0")]
impl TryFrom<$unsigned> for $signed {
type Err = TryFromIntError;

fn try_from(u: $unsigned) -> Result<$signed, TryFromIntError> {
let max = <$signed as FromStrRadixHelper>::max_value() as u64;
if u as u64 > max {
Err(TryFromIntError(()))
} else {
Ok(u as $signed)
}
}
}

#[stable(feature = "rust1", since = "1.0.0")]
impl TryFrom<$signed> for $unsigned {
type Err = TryFromIntError;

fn try_from(u: $signed) -> Result<$unsigned, TryFromIntError> {
let max = <$unsigned as FromStrRadixHelper>::max_value() as u64;
if u < 0 || u as u64 > max {
Err(TryFromIntError(()))
} else {
Ok(u as $unsigned)
}
}
}
)*}
}

cross_sign_from_int_impl!(u8, i8, i16, i32, i64, isize);
cross_sign_from_int_impl!(u16, i8, i16, i32, i64, isize);
cross_sign_from_int_impl!(u32, i8, i16, i32, i64, isize);
cross_sign_from_int_impl!(u64, i8, i16, i32, i64, isize);
cross_sign_from_int_impl!(usize, i8, i16, i32, i64, isize);

#[doc(hidden)]
trait FromStrRadixHelper: PartialOrd + Copy {
fn min_value() -> Self;
fn max_value() -> Self;
fn from_u32(u: u32) -> Self;
fn checked_mul(&self, other: u32) -> Option<Self>;
fn checked_sub(&self, other: u32) -> Option<Self>;
Expand All @@ -2353,6 +2445,7 @@ trait FromStrRadixHelper: PartialOrd + Copy {
macro_rules! doit {
($($t:ty)*) => ($(impl FromStrRadixHelper for $t {
fn min_value() -> Self { Self::min_value() }
fn max_value() -> Self { Self::max_value() }
fn from_u32(u: u32) -> Self { u as Self }
fn checked_mul(&self, other: u32) -> Option<Self> {
Self::checked_mul(*self, other as Self)
Expand Down
1 change: 1 addition & 0 deletions src/libcoretest/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#![feature(unboxed_closures)]
#![feature(unicode)]
#![feature(unique)]
#![feature(try_from)]

extern crate core;
extern crate test;
Expand Down
Loading

0 comments on commit a9779df

Please sign in to comment.