Generic function over integer types

I would like a generic integer function to compare integers to 0 and then return an option depending upon if the integer is greater than zero. Here is my current attempt which doesn't compile:

fn greater_than_zero_or_non<T>(x: T) -> Option<T> 
where
    T: std::cmp::PartialOrd,
{
    if x > 0 {
        Some(x)
    } else {
        None
    }
}

The compiler is complaining about the integer within the comparison:

error[E0308]: mismatched types
  --> src/ledger/address.rs:15:12
   |
15 |     if x > 0 {
   |            ^ expected type parameter, found integer
   |
   = note: expected type `T`
              found type `{integer}`
   = help: type parameters must be constrained to match other types
   = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters

Any help in understanding why this doesn't compile and how one could write something similar without having to use multiple functions for multiple sizes of integers would be amazing.

As I write this I suppose that I could use u64 since all of my input integers are u16 and u32, but I still would like to understand if there's a proper way to accomplish this.

1 Like

You could use num-traits' Zero for this.

2 Likes

Basically the issue is that just because something is ordered doesn't mean there is a meaningful value for zero.

What should zero be if T is String?

3 Likes

@cuviper thank you, for the suggestion and awesome crate. The following function worked:


fn greater_than_zero_or_none<T: Zero + Unsigned>(x: T) -> Option<T> {
    if x.is_zero() {
        None
    } else {
        Some(x)
    }
}

@alice thank you, that makes sense, I was limiting the PartialOrd domain space in my head too much.

Is there something in the standard library which only primitive integers would implement?

No, there isn't. But since all integer types implement Default with a default value of 0, you could just use PartialOrd + Default.

1 Like

Adding Copy gets a little closer to primitives only, at least ruling out things like BigInt.

Much less directly, Range<T>: Iterator only holds for T: Step, an unstable trait that's only implemented for primitive integers in std. That could change though.