Skip to content

Add uN::to_signed and iN::to_unsigned #183

@Shadlock0133

Description

@Shadlock0133

Proposal

Problem statement

Currently, users wanting to change "signed-ness" of an integer while preserving it's bit representation (u8 to i8, or vice versa) would use as conversion. This has problems, as as doesn't restrict its input type, making it possible to introduce bugs during refactors or through code duplication.

Motivation, use-cases

For example, user might have started writing code using u8 type, and needs to use i8 (with wrapping behaviour) in a specific place:

fn foo(x: u8) {
    ...
    bar(x as i8)
    // or
    bar(x as _)
}
// potentially from another crate or from extern block
fn bar(x: i8) { ... }

Later, user decides to change the type to e.g. u16, so foo becomes fn foo(x: u16), but now the code will truncate the value before passing it to bar, potentially introducing subtle bugs. This could be dangerous for low-level and/or unsafe code, where this kind of conversions are not uncommon.

If user used bar(x.to_signed()), they would get an type error instead, making them aware of situation, and forcing them to resolve it in some way.

The proposed solution is also inspired by pointers methods like cast, cast_const, cast_mut, which have similar goals of making it safer to perform conversion without using as.

Solution sketches

My solution is to introduce set of methods on primitive integer type, to_signed and to_unsigned:

impl u8 {
    /// Converts to i8, wrapping if necessary.
    ///
    /// This is equivalent to `as i8`, but is more specific to enhance readability.
    ///
    /// # Example
    /// ```
    /// assert_eq!(255u8.to_signed(), -1i8);
    /// ```
    fn to_signed(self) -> i8 { self as i8 }
}
impl i8 {
    /// Converts to u8, wrapping if necessary.
    ///
    /// This is equivalent to `as u8`, but is more specific to enhance readability.
    ///
    /// # Example
    /// ```
    /// assert_eq!((-1i8).to_unsigned(), 255u8);
    /// ```
    fn to_unsigned(self) -> u8 { self as u8 }
}
// etc.

Other solution could be to use an "inverse" of this, from_signed/from_unsigned, e.g.:

impl u8 {
    fn from_signed(x: i8) -> Self { x as Self }
}

or to add both sets of methods: {to,from}_{signed,unsigned}, but the added value here seems negligible to author.

Links and related work

What happens now?

This issue is part of the libs-api team API change proposal process. Once this issue is filed the libs-api team will review open proposals in its weekly meeting. You should receive feedback within a week or two.

Metadata

Metadata

Assignees

No one assigned

    Labels

    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