-
Notifications
You must be signed in to change notification settings - Fork 20
Integer Manipulation API #204
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
imho |
Can you give a concrete example of code that you'd like to work with that? As far as I am aware, my proposed API is able to perform the same operations. |
pub struct BitVec<UnsignedIntTy>(UnsignedIntTy);
pub trait ShiftOps {
fn shl(self, amount: u32) -> Self;
fn shr(self, amount: u32) -> Self;
fn ashr(self, amount: u32) -> Self;
}
macro_rules! impl_shift_ops {
($t:ty) => {
impl ShiftOps for BitVec<$t> {
fn shl(self, amount: u32) -> Self {
Self(self.0.wrapping_shl(amount))
}
fn shr(self, amount: u32) -> Self {
Self(self.0.wrapping_shr(amount))
}
fn ashr(self, amount: u32) -> Self {
Self(self.0.to_signed().wrapping_shr(amount).to_unsigned())
}
}
};
}
impl_shift_ops!(u8);
impl_shift_ops!(u16);
impl_shift_ops!(u32);
impl_shift_ops!(u64);
impl_shift_ops!(u128);
impl_shift_ops!(usize); |
Two changes I would make to this proposal:
The naming of |
The only thing I can think of is reinterpret, but you could split it into two functions depending on sign: impl u8 {
fn as_signed(self) -> i8;
}
impl i8 {
fn as_unsigned(self) -> u8;
} |
Pointers have Having And that same "this is just changing signedness not the width" sounds handy. Jacob's example above ( |
I made an alternative RFC: Traits for lossy numeric conversions I wasn't aware of this issue, but I would like to exchange ideas and improve my RFC if necessary/desired. |
Several points to consider:
|
Apparently I forgot to leave a comment about this before. I released |
So I came up with a quick idea and implemented it in In other words, the sketch proposed here may remain as-is as long as it doesn't outright forbid |
I think part of this got approved in #359 (comment) as Still worth continuing the discussion here for the other parts. |
There's also my related rust-lang/rfcs#3703, |
I was about to create an ACP for uplifting Given that this topic has come up a few times independently (and signedness conversions implemented in |
Proposal
Problem statement
Logically there are 6 different behaviors that a conversion between two integers may have:
as
-casts implement the first four of these possible behaviors, but can only express one of these behaviors for each pair of typesT as U
.TryFrom
can express behaviors 5 and 6 with the help of some extra code.This API aims to implement all of behaviors 1 through 4 on every possible pair of integer types, using code that more directly expresses the desired behavior and can be combined to express more behaviors.
Motivation, use-cases
Currently, converting between integer types can be done in two ways:
as
-casts, which have a well defined effect for each pair of typesas
casts or the{to,from}_bytes
APIs on different integer types, which also involves making an array larger/smallerOption 1 uses
as
, which can be undesirable due to its leniency in input types, willingness to silently change behavior if types change, and restricted sets of behaviors.Option 2 requires manual bit manipulation, even when that manipulation shouldn't need to be complicated. Even worse it requires expanding or shrinking an array, which is difficult to do concisely.
The ability to express any combination of truncation, zero extending, sign extending, and bit reinterpretation with code that can be checked to do the correct behavior at compile time is better than either of these current solutions, even if it is more wordy.
Use cases:
Solution sketches
In each of these examples, assume that
Self
is an integer type and that the target typeU
is also an integer.(name might need improvement,
bit_cast
?)i8
->u8
,u128
->i128
, etc) because that's the only unambiguous bit cast behavioru8
->i16
ori32
->i64
zero_extend
orsign_extend
instead (should be documented)truncate
instead (should be documented)u8
->u8
), even though it's not very usefulu8
->u16
,i32
->i64
, etc)i8
->u16
,u8
->i8
,u64
->u32
i8
->i16
,i16
->i128
)i8
->i8
,i64
->i16
,i8
->u128
zero_extend
instead (this should be documented)u64
->u16
,i128
->i32
, etc)zero_extend
orsign_extend
instead (should be documented)cast
instead (should be documented)-1_i16
truncated tou8
directly viaas
-casting would be255_u8
. All sign changing behavior should be done with cast:-1_i16.truncate::<i8>().cast::<u8>() == 255_u8
Interactions with
usize
andisize
usize
andisize
have target dependent widths which complicates interactions with them. In the interest of making the methods consistent between targets and not introduce more surprising behavior,usize
andisize
will only be able to be truncated to au8
ori8
and will only be able to be extended from au8
ori8
. Thecast
method will considerusize
andisize
to be the same size as each other, but not the same size as any other type (even if that is true on this target). See below or the full implementation list for more details. The reasoning behind this specific choice is because the minimum possible size forusize
andisize
is 16 bits but there is no maximum size.usize
andisize
therefore cannot be reliably truncated to any type larger than 8 bits (they might not be large enough to truncate) and may not be extended into from any type larger than 8 bits (they might not be large enough to hold the source).usize
andisize
may not be extended into any type because the target type cannot reliably be larger than the source. Even thoughusize
andisize
are always at least 16 bits, they do not have the operation to truncate to 16-bit integers or extend from 16-bit integers because these operations may be a no-op in some cases, but not others.Supported Operations
Click to open (warning: long)
cast
:u8
->u8
i8
->i8
u8
->i8
i8
->u8
u16
->u16
i16
->i16
u16
->i16
i16
->u16
u32
->u32
i32
->i32
u32
->i32
i32
->u32
u64
->u64
i64
->i64
u64
->i64
i64
->u64
u128
->u128
i128
->i128
u128
->i128
i128
->u128
usize
->usize
usize
->isize
isize
->isize
isize
->usize
zero_extend
:u8
->u16
u8
->u32
u8
->u64
u8
->u128
i8
->i16
i8
->i32
i8
->i64
i8
->i128
u16
->u32
u16
->u64
u16
->u128
i16
->i32
i16
->i64
i16
->i128
u32
->u64
u32
->u128
i32
->i64
i32
->i128
u64
->u128
i64
->i128
u8
->usize
i8
->isize
sign_extend
:i8
->i16
i8
->i32
i8
->i64
i8
->i128
i16
->i32
i16
->i64
i16
->i128
i32
->i64
i32
->i128
i64
->i128
i8
->isize
truncate
:u16
->u8
i16
->i8
u32
->u8
u32
->u16
i32
->i8
i32
->i16
u64
->u8
u64
->u16
u64
->u32
i64
->i8
i64
->i16
i64
->i32
u128
->u8
u128
->u16
u128
->u32
u128
->u64
i128
->i8
i128
->i16
i128
->i32
i128
->i64
usize
->u8
isize
->i8
Comparison to
as
-castsFor reference, here are the behaviors of
as
-casts on integer types:i32
->u32
) is a no-op (Rust uses 2's complement for negative values of fixed integers)u32
->u8
) will truncateu8
->u32
) willAll current possible operations using
as
-casts can be replicated with at most two of these functions chained together except for certain operations with usize and isize (see above). Examples (all types explicitly documented, type inference may make this cleaner):u32 as i32
becomesu32.cast::<i32>()
i16 as u8
becomesi16.truncate::<i8>().cast::<u8>()
u128 as u32
becomesu128.truncate::<u32>()
u32 as i64
becomesu32.zero_extend::<u64>().cast::<i64>()
i64 as u128
becomesi64.sign_extend::<i128>().cast::<u128>()
as
-castsi64.zero_extend::<i128>().cast::<u128>()
which zero extends the value rather than sign extends. The equivalent behavior withas
-casts isi64 as u64 as u128
Links and related work
Entirely supersedes #183.edit: this is not entirely true, there's specific macro cases that are much harder to represent.to_signed
andto_unsigned
are representable withcast
.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.
The text was updated successfully, but these errors were encountered: