rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
98.16k stars 12.69k forks source link

Tracking Issue for `integer_sign_cast` #125882

Open Rua opened 5 months ago

Rua commented 5 months ago

Feature gate: #![feature(integer_sign_cast)]

This is a tracking issue for explicit signedness casting methods for integer primitive types. Libs discussion: https://github.com/rust-lang/libs-team/issues/359.

Public API

(for N in [8, 16, 32, 64, 128, size]):

impl uN {
    pub const fn cast_signed(self) -> iN {}
}

impl iN {
    pub const fn cast_unsigned(self) -> uN {}
}

Steps / History

Unresolved Questions

Mostly bikeshedding regarding naming, as mentioned in the Libs discussion. The current proposal follows the naming of cast_const and cast_mut for pointers.

Alternatively, these could be implemented as from_bits and to_bits methods for the signed types.

tbu- commented 5 months ago

Mostly bikeshedding regarding naming, as mentioned in the Libs discussion. The current proposal follows the naming of cast_const and cast_mut for pointers.

I think that cast_signed and cast_unsigned do not communicate intent on what to do on overflows. This is unlike cast_const and cast_mut where no such overflow can occur.

I think something like reinterpret_signed or the proposed from_bits/to_bits woul be more appropriate.

electroCutie commented 5 months ago

do not communicate intent on what to do on overflows

There wouldn't be any overflows, as I understand the term. The number of bits stays the same, which is expressly part of the design of the function.

tbu- commented 5 months ago

It does not communicate what happens to values of unsigned integers that do not fit into the target integer values.

We're reinterpreting the bits as an unsigned integer, but cast does not say that. E.g. casting from integer to float does not reinterpret the bits, casting from i8 to i32 does not solely reinterpret the bits either, it sign-extends them. So "cast" doesn't tell me what happens.

electroCutie commented 5 months ago

@tbu- ah now I see your meaning

Your suggestion of reinterpret_signed seems like a good way to phrase it

Rua commented 5 months ago

What about transmute_signed? That seems closer to Rust's typical jargon.

aapoalas commented 4 months ago

IMO: A consistent API is most important, so from_bits / to_bits should be chosen.

Rua commented 1 month ago

After thinking about this for a while, and looking at how I'm actually using this function, I think I agree with the suggestion to use from_bits and to_bits instead. Do others agree? If so, can I just make a PR to change the name?

jhpratt commented 1 month ago

I personally find from_bits/to_bits to be too similar to the {from|to}_{n|b|l}e_bytes methods. However from_bits and to_bits are present and have been stable for years, with the behavior equivalent to that of a transmute (i.e. the same as this proposal).

So as much as I don't care for the naming, it would be consistent with that. With that said, casting a sign and reinterpreting the type entirely feel like two different things that don't necessarily have precedential impact. An integer and a float share effectively zero in common when using from_bits/to_bits, while integers will share a value for the overlapping range.

aapoalas commented 1 month ago

After thinking about this for a while, and looking at how I'm actually using this function, I think I agree with the suggestion to use from_bits and to_bits instead. Do others agree? If so, can I just make a PR to change the name?

Do you happen to have any examples of this usage in public repositories?

jhpratt commented 1 month ago

@aapoalas While not using this feature directly, the time-rs/time repository uses an API from num-conv that is identical to the current proposal.

SabrinaJewson commented 1 month ago

I’m not too keen on from_bits and to_bits. While it works for the use-case of shuttling an i64 value in a u64-shaped container, it doesn’t look so nice for the dual operation (which I think I find myself doïng more often), since it implies that i64 is your real integer value and u64 is its bitwise representation, when it’s in fact the other way around.

(re some other suggestions: transmute_* reads a danger word in my head, so probably isn’t ideal for a safe method, while reinterpret_* is kinda verbose and doesn’t have precedent. I think that cast does carry a sufficient connotation of reinterpreting the bit values directly; it’s in line with pointers’ cast method too.)

electroCutie commented 3 weeks ago

What about something like bits_to_i / bits_to_u (and if desired the bits_from_* versions in associated functions)

Hints that it is bitwise and doesn't introduce a new term (I take it as a given that someone performing this operation already knows the term "bits"), doesn't try to put one or the other as the canonical representation, still uses the familiar from/to jargon, and is a bit less verbose than some other wordier proposals