Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compare a generic number argument with a constant

Tags:

generics

rust

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?

like image 685
Gunchars Avatar asked Jan 12 '14 10:01

Gunchars


2 Answers

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
like image 125
telotortium Avatar answered Oct 23 '22 13:10

telotortium


You could try this

compare<T: PartialOrd<i32>>(n: T) -> bool {
  n > 42
}

expression lhs > rhs is equal to <T as PartialOrd<U>>::gt(&lhs, &rhs)
where lhs has type T and rhs has type U
Thus T has to implement PartialOrd in order to allow use of operator >

like image 32
user2065323 Avatar answered Oct 23 '22 14:10

user2065323