-
Notifications
You must be signed in to change notification settings - Fork 24
Description
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.