How to distinguish signed & unsigned integers?

For weight_matchers I have a macro that takes any primitive number. It needs to treat floats, integers and unsigneds slightly differently. To that end the type must mostly be given explicitly. I want to change that to leverage inferrence.

Despite not knowing the input-type, I can derive values for some numbers, so that I can test symbolically, independent of syntactical differences:

if one / two > zero
    // float
else if zero.saturating_sub(one) < zero
    // integer
else
    // unsigned

The 1st test makes it so that the 2nd test will only ever see integral types. Sadly the compiler doesn’t know that. So when the macro inputs are floats, it’ll look for that method on the float (which sadly doesn’t have it.)

I’m racking my brain for a formula akin to the 1st, which will not panic on overflow, while letting me distinguish signedness. I’ve also tried various generic approaches. But even if I could distinguish on <T: !Neg>, I can’t find a way.

To make this harder, it’s a feature of this crate to be able to do everything in const. Additionally I would not want to impl any trait for all primitive types, because floats currently have unstable sizes, and integers might get arbitrary bit sizes.

Currently, there is no way to write the behavior you are looking for (“don't compile a call to saturating_sub unless this is an integer type”) without involving a trait. (This is wrong; see below.)

You can use a wrapper type with const inherent methods -- you'll have to make that pub if it's used in an exported macro, but you can add #[doc(hidden)] at least. Something like:

struct Query<T>(T);

impl Query<f32> {
    const fn is_float(&self) -> bool {
        true
    }
    const fn is_signed(&self) -> bool {
        false
    }
}
// etc.

Here's a playground using it in a const block in a macro.

I used explicit type suffixes on the primitive values though, so you still need something that gets the compiler to infer the type before you try to call these inherent methods.

3 Likes

Can you explain “floats currently have unstable sizes”? I think it is probably not true, but I am not sure exactly what you mean.

Oh, you mean that f16 and f128 are unstable, sorry.

I don't think you're going to find a solution that is both const and works for new numeric types added in the future — until const traits are available.

1 Like