Skip to content

Commit

Permalink
Add cast function to primitive integers
Browse files Browse the repository at this point in the history
This commit is a result of the libs team's discussion of #33417 and how it
affects integral types. The conclusion was that the motivation for converting
integral types, working in a cross-platform code that uses platform-specific
integer types, was different enough from the intent of `TryFrom` that it doesn't
make sense to unify the paths. As a result this is a proposal for the
alternative version of the API which purely works with integral types.

An unstable `Cast` trait is added as the implementation detail of this API, and
otherwise with this you should be able to call `i32::cast(0u8)` at will. The
intention is then to call this in platform-specific contexts like:

    // Convert from C to Rust
    let a: c_int = ...;
    let b: u32 = u32::cast(a)?;

    // Convert from Rust to C
    let a: u32 = ...;
    let b: c_int = <c_int>::cast(a)?;

Everything here is unstable for now, but the intention is that this will
stabilize sooner than `TryFrom`.
  • Loading branch information
alexcrichton committed Jun 21, 2017
1 parent 4450779 commit a6b0068
Show file tree
Hide file tree
Showing 9 changed files with 247 additions and 90 deletions.
8 changes: 8 additions & 0 deletions src/doc/unstable-book/src/library-features/num_cast.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# `num_cast`

The tracking issue for this feature is: [#FIXME]

[#FIXME]: https://github.com/rust-lang/rust/issues/FIXME

------------------------

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# `num_cast_internals`

The tracking issue for this feature is: [#FIXME]

[#FIXME]: https://github.com/rust-lang/rust/issues/FIXME

------------------------

99 changes: 99 additions & 0 deletions src/libcore/num/cast.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use fmt;

/// Internal trait for APIs like `i32::cast`.
#[unstable(feature = "num_cast_internals", issue = "0")]
pub trait Cast<T>: Sized {
/// Internal implementation detail of this trait.
fn cast(t: T) -> Result<Self, CastError>;
}

/// Error type returned from APIs like `i32::cast`, indicates that a cast could
/// not be performed losslessly.
#[unstable(feature = "num_cast", issue = "0")]
#[derive(Debug)]
pub struct CastError(());

#[unstable(feature = "num_cast", issue = "0")]
impl fmt::Display for CastError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
"failed to losslessly cast integral types".fmt(f)
}
}

macro_rules! same_sign_cast_int_impl {
($storage:ty, $target:ty, $($source:ty),*) => {$(
#[unstable(feature = "num_cast", issue = "0")]
impl Cast<$source> for $target {
#[inline]
fn cast(u: $source) -> Result<$target, CastError> {
let min = <$target>::min_value() as $storage;
let max = <$target>::max_value() as $storage;
if u as $storage < min || u as $storage > max {
Err(CastError(()))
} else {
Ok(u as $target)
}
}
}
)*}
}

same_sign_cast_int_impl!(u128, u8, u8, u16, u32, u64, u128, usize);
same_sign_cast_int_impl!(i128, i8, i8, i16, i32, i64, i128, isize);
same_sign_cast_int_impl!(u128, u16, u8, u16, u32, u64, u128, usize);
same_sign_cast_int_impl!(i128, i16, i8, i16, i32, i64, i128, isize);
same_sign_cast_int_impl!(u128, u32, u8, u16, u32, u64, u128, usize);
same_sign_cast_int_impl!(i128, i32, i8, i16, i32, i64, i128, isize);
same_sign_cast_int_impl!(u128, u64, u8, u16, u32, u64, u128, usize);
same_sign_cast_int_impl!(i128, i64, i8, i16, i32, i64, i128, isize);
same_sign_cast_int_impl!(u128, u128, u8, u16, u32, u64, u128, usize);
same_sign_cast_int_impl!(i128, i128, i8, i16, i32, i64, i128, isize);
same_sign_cast_int_impl!(u128, usize, u8, u16, u32, u64, u128, usize);
same_sign_cast_int_impl!(i128, isize, i8, i16, i32, i64, i128, isize);

macro_rules! cross_sign_cast_int_impl {
($unsigned:ty, $($signed:ty),*) => {$(
#[unstable(feature = "num_cast", issue = "0")]
impl Cast<$unsigned> for $signed {
#[inline]
fn cast(u: $unsigned) -> Result<$signed, CastError> {
let max = <$signed>::max_value() as u128;
if u as u128 > max {
Err(CastError(()))
} else {
Ok(u as $signed)
}
}
}

#[unstable(feature = "num_cast", issue = "0")]
impl Cast<$signed> for $unsigned {
#[inline]
fn cast(u: $signed) -> Result<$unsigned, CastError> {
let max = <$unsigned>::max_value() as u128;
if u < 0 || u as u128 > max {
Err(CastError(()))
} else {
Ok(u as $unsigned)
}
}
}
)*}
}

cross_sign_cast_int_impl!(u8, i8, i16, i32, i64, i128, isize);
cross_sign_cast_int_impl!(u16, i8, i16, i32, i64, i128, isize);
cross_sign_cast_int_impl!(u32, i8, i16, i32, i64, i128, isize);
cross_sign_cast_int_impl!(u64, i8, i16, i32, i64, i128, isize);
cross_sign_cast_int_impl!(u128, i8, i16, i32, i64, i128, isize);
cross_sign_cast_int_impl!(usize, i8, i16, i32, i64, i128, isize);
32 changes: 32 additions & 0 deletions src/libcore/num/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ impl<T: fmt::UpperHex> fmt::UpperHex for Wrapping<T> {
}

mod wrapping;
mod cast;

#[unstable(feature = "num_cast", issue = "0")]
pub use self::cast::*;

// All these modules are technically private and only exposed for coretests:
pub mod flt2dec;
Expand Down Expand Up @@ -127,6 +131,20 @@ macro_rules! int_impl {
!Self::min_value()
}

/// Attempt to cast the provided integral type into this type.
///
/// # Errors
///
/// This function will return an error if the provided integral type
/// cannot losslessly be represented as this type.
#[unstable(feature = "num_cast", issue = "0")]
#[inline]
pub fn cast<T>(t: T) -> Result<Self, CastError>
where Self: Cast<T>
{
Cast::cast(t)
}

/// Converts a string slice in a given base to an integer.
///
/// Leading and trailing whitespace represent an error.
Expand Down Expand Up @@ -1290,6 +1308,20 @@ macro_rules! uint_impl {
#[inline]
pub const fn max_value() -> Self { !0 }

/// Attempt to cast the provided integral type into this type.
///
/// # Errors
///
/// This function will return an error if the provided integral type
/// cannot losslessly be represented as this type.
#[unstable(feature = "num_cast", issue = "0")]
#[inline]
pub fn cast<T>(t: T) -> Result<Self, CastError>
where Self: Cast<T>
{
Cast::cast(t)
}

/// Converts a string slice in a given base to an integer.
///
/// Leading and trailing whitespace represent an error.
Expand Down
1 change: 1 addition & 0 deletions src/libcore/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#![feature(libc)]
#![feature(nonzero)]
#![feature(ord_max_min)]
#![feature(num_cast)]
#![feature(rand)]
#![feature(raw)]
#![feature(sip_hash_13)]
Expand Down
Loading

0 comments on commit a6b0068

Please sign in to comment.