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

for conversions, resulting in code whose correctness is not verified by the compiler Introduce platform-dependent impls of From and Into - 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