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?
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
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 >
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With