Skip to content

Commit a6b0068

Browse files
committed
Add cast function to primitive integers
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`.
1 parent 4450779 commit a6b0068

File tree

9 files changed

+247
-90
lines changed

9 files changed

+247
-90
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# `num_cast`
2+
3+
The tracking issue for this feature is: [#FIXME]
4+
5+
[#FIXME]: https://github.com/rust-lang/rust/issues/FIXME
6+
7+
------------------------
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# `num_cast_internals`
2+
3+
The tracking issue for this feature is: [#FIXME]
4+
5+
[#FIXME]: https://github.com/rust-lang/rust/issues/FIXME
6+
7+
------------------------
8+

src/libcore/num/cast.rs

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use fmt;
12+
13+
/// Internal trait for APIs like `i32::cast`.
14+
#[unstable(feature = "num_cast_internals", issue = "0")]
15+
pub trait Cast<T>: Sized {
16+
/// Internal implementation detail of this trait.
17+
fn cast(t: T) -> Result<Self, CastError>;
18+
}
19+
20+
/// Error type returned from APIs like `i32::cast`, indicates that a cast could
21+
/// not be performed losslessly.
22+
#[unstable(feature = "num_cast", issue = "0")]
23+
#[derive(Debug)]
24+
pub struct CastError(());
25+
26+
#[unstable(feature = "num_cast", issue = "0")]
27+
impl fmt::Display for CastError {
28+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
29+
"failed to losslessly cast integral types".fmt(f)
30+
}
31+
}
32+
33+
macro_rules! same_sign_cast_int_impl {
34+
($storage:ty, $target:ty, $($source:ty),*) => {$(
35+
#[unstable(feature = "num_cast", issue = "0")]
36+
impl Cast<$source> for $target {
37+
#[inline]
38+
fn cast(u: $source) -> Result<$target, CastError> {
39+
let min = <$target>::min_value() as $storage;
40+
let max = <$target>::max_value() as $storage;
41+
if u as $storage < min || u as $storage > max {
42+
Err(CastError(()))
43+
} else {
44+
Ok(u as $target)
45+
}
46+
}
47+
}
48+
)*}
49+
}
50+
51+
same_sign_cast_int_impl!(u128, u8, u8, u16, u32, u64, u128, usize);
52+
same_sign_cast_int_impl!(i128, i8, i8, i16, i32, i64, i128, isize);
53+
same_sign_cast_int_impl!(u128, u16, u8, u16, u32, u64, u128, usize);
54+
same_sign_cast_int_impl!(i128, i16, i8, i16, i32, i64, i128, isize);
55+
same_sign_cast_int_impl!(u128, u32, u8, u16, u32, u64, u128, usize);
56+
same_sign_cast_int_impl!(i128, i32, i8, i16, i32, i64, i128, isize);
57+
same_sign_cast_int_impl!(u128, u64, u8, u16, u32, u64, u128, usize);
58+
same_sign_cast_int_impl!(i128, i64, i8, i16, i32, i64, i128, isize);
59+
same_sign_cast_int_impl!(u128, u128, u8, u16, u32, u64, u128, usize);
60+
same_sign_cast_int_impl!(i128, i128, i8, i16, i32, i64, i128, isize);
61+
same_sign_cast_int_impl!(u128, usize, u8, u16, u32, u64, u128, usize);
62+
same_sign_cast_int_impl!(i128, isize, i8, i16, i32, i64, i128, isize);
63+
64+
macro_rules! cross_sign_cast_int_impl {
65+
($unsigned:ty, $($signed:ty),*) => {$(
66+
#[unstable(feature = "num_cast", issue = "0")]
67+
impl Cast<$unsigned> for $signed {
68+
#[inline]
69+
fn cast(u: $unsigned) -> Result<$signed, CastError> {
70+
let max = <$signed>::max_value() as u128;
71+
if u as u128 > max {
72+
Err(CastError(()))
73+
} else {
74+
Ok(u as $signed)
75+
}
76+
}
77+
}
78+
79+
#[unstable(feature = "num_cast", issue = "0")]
80+
impl Cast<$signed> for $unsigned {
81+
#[inline]
82+
fn cast(u: $signed) -> Result<$unsigned, CastError> {
83+
let max = <$unsigned>::max_value() as u128;
84+
if u < 0 || u as u128 > max {
85+
Err(CastError(()))
86+
} else {
87+
Ok(u as $unsigned)
88+
}
89+
}
90+
}
91+
)*}
92+
}
93+
94+
cross_sign_cast_int_impl!(u8, i8, i16, i32, i64, i128, isize);
95+
cross_sign_cast_int_impl!(u16, i8, i16, i32, i64, i128, isize);
96+
cross_sign_cast_int_impl!(u32, i8, i16, i32, i64, i128, isize);
97+
cross_sign_cast_int_impl!(u64, i8, i16, i32, i64, i128, isize);
98+
cross_sign_cast_int_impl!(u128, i8, i16, i32, i64, i128, isize);
99+
cross_sign_cast_int_impl!(usize, i8, i16, i32, i64, i128, isize);

src/libcore/num/mod.rs

+32
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ impl<T: fmt::UpperHex> fmt::UpperHex for Wrapping<T> {
8888
}
8989

9090
mod wrapping;
91+
mod cast;
92+
93+
#[unstable(feature = "num_cast", issue = "0")]
94+
pub use self::cast::*;
9195

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

134+
/// Attempt to cast the provided integral type into this type.
135+
///
136+
/// # Errors
137+
///
138+
/// This function will return an error if the provided integral type
139+
/// cannot losslessly be represented as this type.
140+
#[unstable(feature = "num_cast", issue = "0")]
141+
#[inline]
142+
pub fn cast<T>(t: T) -> Result<Self, CastError>
143+
where Self: Cast<T>
144+
{
145+
Cast::cast(t)
146+
}
147+
130148
/// Converts a string slice in a given base to an integer.
131149
///
132150
/// Leading and trailing whitespace represent an error.
@@ -1290,6 +1308,20 @@ macro_rules! uint_impl {
12901308
#[inline]
12911309
pub const fn max_value() -> Self { !0 }
12921310

1311+
/// Attempt to cast the provided integral type into this type.
1312+
///
1313+
/// # Errors
1314+
///
1315+
/// This function will return an error if the provided integral type
1316+
/// cannot losslessly be represented as this type.
1317+
#[unstable(feature = "num_cast", issue = "0")]
1318+
#[inline]
1319+
pub fn cast<T>(t: T) -> Result<Self, CastError>
1320+
where Self: Cast<T>
1321+
{
1322+
Cast::cast(t)
1323+
}
1324+
12931325
/// Converts a string slice in a given base to an integer.
12941326
///
12951327
/// Leading and trailing whitespace represent an error.

src/libcore/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#![feature(libc)]
2929
#![feature(nonzero)]
3030
#![feature(ord_max_min)]
31+
#![feature(num_cast)]
3132
#![feature(rand)]
3233
#![feature(raw)]
3334
#![feature(sip_hash_13)]

0 commit comments

Comments
 (0)