将通用数字参数与常量进行比较

时间:2014-01-12 10:43:15

标签: generics rust

假设我有一个函数将一个数字参数与一个常量进行比较并返回一个布尔值:

fn compare(n: f64) -> bool {
  n > 42 as f64
}

这很好用,但我似乎无法使它成为通用的:

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`
}

你将如何实现这一目标?

2 个答案:

答案 0 :(得分:10)

正如您所见,Rust as运算符在它允许的强制转换中非常有限。根据{{​​3}},

  

可以将数值转换为任何数字类型。原始指针值可以转换为任何整数类型或原始指针类型。任何其他演员表都不受支持,无法编译。

此外,Rust不会执行任何类型的隐式运行时数字强制,因此您必须将比较运算符的参数显式强制转换为相同的类型(因为Ord定义了lt原型fn lt(&self, other: &Self))的方法。

这引出了一个有趣的观点 - <函数中compare运算符的参数应该转换为Tint(假定的类型)类型42)?在这种情况下,您希望在转换为n后将42与值T进行比较。在保持通用性的同时实现此目的的最直接方法是要求T实现外部Rust manual中包含的FromPrimitive特征,该特征提供了获取类型{{1}的值的方法来自T(或其他Rust原始数字类型)。您的int函数可以这样写:

compare


为了测试这一点,我创建了一个简单的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") } 类型,它将二进制数表示为BinaryNumber的数组:

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
            }
        }
    }
}

打印

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));
}

答案 1 :(得分:2)

你可以试试这个

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

表达 lhs&gt; rhs 等于&lt; T as PartialOrd&lt; U&gt;&gt; :: gt(&amp; lhs,&amp; rhs)
其中 lhs 的类型为 T rhs 的类型为 U
因此 T 必须实施 PartialOrd 才能允许使用运营商&gt;