Skip to content

Explicit signedness casts for integers #359

Closed
@Rua

Description

@Rua

Proposal

Problem statement

Casting using the as operator has some issues, in that it does not necessarily reflect the intent of the cast. Was the cast meant to be lossy or not? Was it meant to preserve the size? Constness? Signedness?

Some of the motivations for pointer constness casts raised at https://internals.rust-lang.org/t/casting-constness-can-be-risky-heres-a-simple-fix/15933 would apply here too. When refactoring, the sizes of types may change, and silently introduce bugs due to casts not being updated.

Clippy has the cast_possible_wrap and cast_sign_loss lints, which tag sign-changing casts, but since there is no alternative to using as, it cannot propose a fix.

Motivating examples or use cases

Rust has been slowly introducing alternatives to direct casts, to more clearly signal intent, catch errors, and allow casting only one aspect of a type instead of everything in one go. From is used when the cast is lossless, TryFrom for checked casts, ptr::cast_const and ptr::cast_mut change the mutability and nothing else, ptr::cast changes pointed type and nothing else.

Solution sketch

Following the example of ptr::cast_const and ptr::cast_mut, I propose adding two new methods to integer types, for all X including size:

  • uX::cast_signed() -> iX
  • iX::cast_unsigned() -> uX

These do the same as a regular as cast, but crucially they only cast to another integer of the same size and opposite signedness. They don't ever change the size, so i32u64 and u64i32 are not implemented.

Alternatives

Other names for the two proposed methods would be possible, such as:

  • reinterpret_*, following the example of C++.
  • transmute_*, since this is really a kind of transmute.
  • cast_sign for both methods. This has the downside of not conveying which direction you're casting in, which the pointer constness methods do indicate.
  • iX::from_bits and iX::to_bits, following the example of floats. The methods would only be present on signed integers in this case.

It would also be possible to implement this as a trait, like the explicit_cast crate does, but since it's a closed class of types, that seems overkill. There have also been proposals to include this under a more general "wrapping cast" or "lossy cast" function/trait of some sort, but I feel that a i32u32 cast isn't in the same class of casts as i32 -> i16, since the former is fully reversible and the latter is actually lossy.

Links and related work

Metadata

Metadata

Assignees

No one assigned

    Labels

    ACP-acceptedAPI Change Proposal is accepted (seconded with no objections)T-libs-apiapi-change-proposalA proposal to add or alter unstable APIs in the standard libraries

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions