-
Feature Name: platform_from
-
Start Date: (fill me in with today's date, YYYY-MM-DD)
-
RFC PR: (leave this empty)
-
Rust Issue: (leave this empty)
Summary
Add the following traits to core::convert
and std::convert
, which are identical in structure to the existing From
and Into
traits:
pub trait PlatformFrom<T>: Sized {
fn platform_from(_: T) -> Self;
}
pub trait PlatformInto<T>: Sized {
fn platform_into(self) -> T;
}
impl<T, U> PlatformFrom<T> for U where U: From<T> { ... }
impl<T, U> PlatformInto<U> for T where U: PlatformFrom<T> { ... }
In addition, U: PlatformFrom<T>
for all numeric primitives T
and U
such that the conversion is valid on the current target platform. For example, usize: PlatformFrom<u32>
on 32- and 64-bit platforms.
Motivation
The existing From and Into traits are only defined for conversions which are valid on all target platforms. This means that they are unavailable to programmers developing platform-specific code even if a given conversion is valid on the target platform. For example, code written for a 32-bit platform cannot convert from a u32
into a usize
using these traits since the conversion is not valid on all platforms.
This forces programmers in this situation to resort to a combination of the as
keyword and comments explaining why the conversion is guaranteed to be lossless (example). Since there is no compiler assistance, if the conversion becomes lossy in the future because the code is incorrectly ported to a different architecture, or used in a platform-agnostic context, etc, compilation will continue to succeed when it shouldn't, and the code may be wrong. In the context of unsafe
code, this can lead to undefined behavior. In the context of safe code, it can still lead to unspecified behavior.
Guide-level explanation
In the core::convert
and std::convert
modules, the following traits are added:
pub trait PlatformFrom<T>: Sized {
fn platform_from(_: T) -> Self;
}
pub trait PlatformInto<T>: Sized {
fn platform_into(self) -> T;
}
impl<T, U> PlatformFrom<T> for U where U: From<T> { ... }
impl<T, U> PlatformInto<U> for T where U: PlatformFrom<T> { ... }
In addition, U: PlatformFrom<T>
for all numeric primitives T
and U
such that the conversion is valid on the current target platform. Note that, in practice, conversions between primitives whose size is not platform-dependent are already covered by existing From
impls. Thus, the only added implementations are to or from usize
, isize
, *const T
, and *mut T
.
These traits are intended to be used only in a platform-dependent context. In that context, their use is encouraged instead of the as
keyword. When they are used, code which is either buggy or incorrectly ported to a different platform will stop compiling as soon as conversions which were supposed to be infallible become fallible.
Reference-level explanation
Concretely, U: PlatformFrom<T>
for the following types (other valid conversions like u8 -> usize
are covered by existing From
implementations, and thus receive a blanket PlatformFrom
implementation):
32-bit platforms
T |
U |
---|---|
u16 |
isize |
u32 |
usize |
i32 |
isize |
usize |
u32 |
usize |
u64 |
usize |
i64 |
usize |
u128 |
usize |
i128 |
isize |
i32 |
isize |
i64 |
isize |
i128 |
64-bit platforms
T |
U |
---|---|
u16 |
isize |
u32 |
usize |
u32 |
isize |
i32 |
isize |
u64 |
usize |
i64 |
isize |
usize |
u64 |
usize |
u128 |
usize |
i128 |
isize |
i64 |
isize |
i128 |
In addition, for every T: PlatformFrom<usize>
and usize: PlatformFrom<T>
conversion listed in the preceding two tables, two more impls exist for *const U
and *mut U
, as const and mut raw pointers have the same memory layout as usize
.
Drawbacks
This adds more platform-dependent APIs to core
and std
, which is generally something we try to avoid.
Rationale and alternatives
The two primary alternatives that the author is aware of are:
- Continue with the status quo - this is undesirable because it leads, in practice, to programmers using
as
for conversions, resulting in code whose correctness is not verified by the compiler - Introduce platform-dependent impls of
From
andInto
- this has been discussed, and its primary drawback is that it allows code to accidentally become platform-dependent
The design presented in this RFC avoids the problems with both of these alternatives by a) providing the authors of platform-dependent code a mechanism for conversion which provides compiler verification that conversions are lossless and, b) designing the API in a way that will discourage platform-agnostic code from accidentally using it and thus becoming dependent on platform-specific behavior.
Prior art
N/A
Unresolved questions
-
Where should these traits live?
{core,std}::convert
seems natural because of the existingFrom
andInto
traits, but those modules do not currently have any platform-dependent APIs. -
What should these traits (and their methods) be named?
-
Are there any other impls that should be provided besides those listed in the Reference section?