Compare a generic number argument with a constant

generics, rust

Solution

As you've seen, the Rust `as` operator is very limited in the casts it allows. According to the Rust manual,

A numeric value can be cast to any numeric type. A raw pointer value can be cast to or from any integral type or raw pointer type. Any other cast is unsupported and will fail to compile.

Furthermore, Rust doesn't do any sort of implicit run-time numeric coercion, so you have to explicitly coerce the arguments of the comparison operators to the same type (since `Ord` defines an `lt` method with the prototype `fn lt(&self, other: &Self)`).

This brings up an interesting point -- to which type should the arguments to the `<` operator in your `compare` function be cast, `T` or `int` (the presumed type of `42`)? It seems in this case that you want to compare `n` to the value `42` after conversion to `T`. The most straightforward way to accomplish this while remaining generic is to require that `T` implement the `FromPrimitive` trait, contained in the external `num` crate, which provides methods from obtaining a value of type `T` from an `int` (or other Rust primitive numeric types). Your `compare` function can then be written like this:

extern crate num;

use num::FromPrimitive;

fn compare<T: Ord + FromPrimitive>(n: T) -> bool {
    n > FromPrimitive::from_int(42).expect("42 must be convertible to type of n")
}

To test this out, I created a simple `BinaryNumber` type that represents a binary number as an array of `bool`:

use std::num::abs;

type Bits = [bool, ..64];

struct BinaryNumber {
    priv negbit: bool,
    priv bits: Bits,
}

fn bits_from_u64(n: u64) -> Bits {
    let mut bits = [false, ..64];
    for i in range(0u, 64u) {
        if ((1 << i) & n) != 0 {
            bits[i] = true;
        }
    }
    bits
}

impl FromPrimitive for BinaryNumber {
    fn from_u64(n: u64) -> Option<BinaryNumber> {
        Some(BinaryNumber {
                negbit: false,
                bits: bits_from_u64(n.to_u64().unwrap())
        })
    }
    fn from_i64(n: i64) -> Option<BinaryNumber> {
        Some(BinaryNumber {
                negbit: n < 0,
                bits: bits_from_u64(abs(n).to_u64().unwrap())
        })
    }
}

impl Eq for BinaryNumber {
    fn eq(&self, other: &BinaryNumber) -> bool {
        if self.negbit != other.negbit { return false }
        for i in range(0, 64).map(|i| 64 - 1 - i) {
            if self.bits[i] != other.bits[i] {
                return false;
            }
        }
        true
    }
}

impl Ord for BinaryNumber {
    fn lt(&self, other: &BinaryNumber) -> bool {
        match (self.negbit, other.negbit) {
            (true, false) => true,
            (false, true) => false,
            _             => {
                let neg = self.negbit;
                for i in range(0, 64).map(|i| 64 - 1 - i) {
                    if neg && self.bits[i] && !other.bits[i] {
                        return true;
                    } else if !self.bits[i] && other.bits[i] {
                        return true;
                    }
                }
                false
            }
        }
    }
}

Then then following code

fn main() {
    let x: BinaryNumber = FromPrimitive::from_int(0).unwrap();
    let y: BinaryNumber = FromPrimitive::from_int(42).unwrap();
    let z: BinaryNumber = FromPrimitive::from_int(100).unwrap();
    println!("compare(x) = {}", compare(x));
    println!("compare(y) = {}", compare(y));
    println!("compare(z) = {}", compare(z));
}

prints

compare(x) = false
compare(y) = false
compare(z) = true

Problem

Suppose I have a function that compares a number argument to a constant and returns a boolean: ``` fn compare(n: f64) -> bool { n > 42 as f64 } ``` This works fine, but I can't seem to make it generic: ``` fn compare<T: Ord>(n: T) -> bool { n > 42 as T // error: non-scalar cast: `<VI0>` as `T` } fn compare<T: Ord>(n: T) -> bool { n > 42 // mismatched types: expected `T` but found `<VI0>` (expected type parameter but found integral variable) } fn compare<T: Num>(n: T) -> bool { n > 42 // error: binary operation > cannot be applied to type `T` } ``` How would you accomplish this?

Original source