- 
                Notifications
    You must be signed in to change notification settings 
- Fork 24
Description
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 i32 → u64 and u64 → i32 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_signfor both methods. This has the downside of not conveying which direction you're casting in, which the pointer constness methods do indicate.
- iX::from_bitsand- 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 i32 → u32 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
- Community Discord discussion: https://discord.com/channels/273534239310479360/957720175619215380/1221934905844432996
- Internals: Let's deprecate asfor lossy numeric casts
- Internals: Pre-RFC: Add explicitly-named numeric conversion APIs
- Reddit: Generically converting integers between signed and unsigne
- explicit_castcrate: https://docs.rs/explicit_cast/latest/explicit_cast/trait.SignCast.html
- RFC: Conversions: FromLossy and TryFromLossy traits