Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cast function to primitive integers #42456

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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(());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this separate error really needed? Why not an option based API? The ? operator is about to work with options as well, no?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intention is that you can define From<CastError> for MyError and have ? work in result-based functions, whereas in option base dfunctions you can just use cast(..).ok()?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IDK, for me the operation feels similar to checked_add and that returns an option as well. Or is the policy to return Result everywhere for new APIs?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's pretty difficult to create a blanket policy that works everywhere, but this is intended for use in APIs that are already dealing with calls to libc and such and are almost guaranteed to already be returning Result.


#[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