Description
From all the integer traits present in std::num
, only SignedInt
includes Neg
. The Neg
trait is not implemented by Int
and UnsignedInt
types because, presumably, it does not make sense to negate unsigned integers. I will not argue whether this is a good policy or not.
However, this makes writing generic code for unsigned types harder than it needs to be. For example, suppose we have the function
fn f(x: u32) -> u32 { -(x >> 31) }
This function works perfectly (albeit with a warning, but it is valid Rust as far as I can tell) when implemented with a concrete unsigned integer type. However, when making the function generic it stops working:
fn f<T: UnsignedInt>(x: T) -> T { -(x >> (size_of::<T>()*8 - 1)) }
// error: cannot apply unary operator `-` to type `T`
Changing this to 0 - <expression>
is a simple solution, but one that also does not work very well without pulling other (NumCast
, FromPrimitive
) traits. Another solution is to pull in the Neg
trait directly, but this increases verbosity due to Neg
's associated type machinery. All in all, the lack of unsigned negation complicates things.
Now, I accept that you may not want to make unsigned negation common or encouraged behavior. But in that case it also makes no sense to have the -
operator available at all for unsigned types; users that really want such semantics can replace it by 0-<expression>
in non-generic code.
My overall point is:
- If
u32
,usize
, etc allow negation, i.e., have theNeg
type implemented, so shouldUnsignedInt
; - If
UnsignedInt
is not meant to be negatable, neither should beu32
,usize
, etc.