Std::cmp (Comparison Module) Type Restrictions and Bespoke Explicit Type Checking


#1

Why are developers restricted to only using arguments of type i32 for matching with the std::cmp (Comparison and Ordering Module)?

I have tried to be explicit as possible in declaring variables secret_number and guess as being of type u64 (unsigned 64 bit integer literal). However, it will not allow me to explicitly define the type of arguments and output to expect from the match cmp function (i.e. none of the following approaches are allowable: match cmp(guess: u64, secret_number: u64) -> { ... and Ordering::Less::<u64> => ... and Ordering::Less -> u64 => ...)

Given the following code snippet

  let secret_number: u64 = (rand::random::<u64>());
  let guess: u64 = (rand::random::<u64>());

  match cmp(guess, secret_number) {
      Ordering::Less => println!("Too small"),
      Ordering::Greater => println!("Too big!"),
      Ordering::Equal => println!("Win"),
  }

Building with Cargo gives the following errors:

src/main.rs:426:13: 426:18 error: mismatched types:
 expected `i32`,
    found `u64`
(expected i32,
    found u64) [E0308]
src/main.rs:426   match cmp(guess, secret_number) {
                            ^~~~~
src/main.rs:426:20: 426:33 error: mismatched types:
 expected `i32`,
    found `u64`
(expected i32,
    found u64) [E0308]
src/main.rs:426   match cmp(guess, secret_number) {

Note that I am running rustc 1.0.0-nightly


#2

I worked out the reason why it was restricting the use of the std::cmp Comparison and Ordering Module to only i32 types.

It was because I had previously declared and configured the cmp Function with the Ordering Enum and some namespaced Variants before the fn main() { of my Rust code file main.rs a couple of weeks ago and I had used it at the time to compare two integers of i32 type.

fn cmp(a: i32, b: i32) -> Ordering {
  if a < b      { Ordering::Less }
  else if a > b { Ordering::Greater }
  else          { Ordering::Equal }
}

When I attempted to reuse this cmp Function again today for the the purpose of comparing two integers of u64 type (Unsigned 64-bit integer
0 to 2^64-1) instead, I had forgotten that I had declared it to expect only arguments of i32 type (Signed 32-bit integer
-2^31 to 2^31-1). So, to fix the issue I just changed the first line to expect u64 instead (which is ok since I wasn’t dealing with negative numbers when using i32 previously anyway):

fn cmp(a: u64, b: u64) -> Ordering {
...

So, my question now is, is it possible to declare multiple permitted types in Rust similar to the following fake code example (where the fake part is <i8, u64>) in order to achieve a “custom” acceptable range of input values?
i.e. where argument a would accept i8 (signed 8-bit integers
-2^7 to 2^7-1, and also accept u64 (unsigned 64-bit integer
0 to 2^64-1), so ultimately a would only accept the Inner Join range of the the two types (i.e. -2^7 to 2^64-1), whilst b would only accept say 64-bit floating point numbers or something.

fn cmp(a: <i8, u64>, b: f64) -> Ordering {

#3

You can do this kind of “overloading” using traits.

trait ComparableToF64 {
    fn cmp(self, other: f64) -> Ordering;
}
impl ComparableToF64 for u64 {
    fn cmp(self, other: f64) -> Ordering {
        // IMPLEMENT HERE!
        unimplemented!();
    }
}
impl ComparableToF64 for i8 {
    fn cmp(self, other: f64) -> Ordering {
        // IMPLEMENT HERE!
        unimplemented!();
    }
}
fn cmp<T:ComparableToF64>(a: T, b: f64) -> Ordering { a.cmp(b) }