Skip to content

Numeric traits inconsistent with integer type system #21069

Description

@sneves

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 the Neg type implemented, so should UnsignedInt;
  • If UnsignedInt is not meant to be negatable, neither should be u32, usize, etc.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions