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

Gun*_*ars 12 generics rust

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

fn compare(n: f64) -> bool {
  n > 42 as f64
}
Run Code Online (Sandbox Code Playgroud)

这工作正常,但我似乎无法使其通用:

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`
}
Run Code Online (Sandbox Code Playgroud)

你会怎么做到这一点?

tel*_*ium 10

正如您所见,Rust as运算符在它允许的强制转换中非常有限.根据Rust手册,

数值可以转换为任何数字类型.原始指针值可以转换为任何整数类型或原始指针类型.任何其他强制转换都不受支持,无法编译.

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

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

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")
}
Run Code Online (Sandbox Code Playgroud)


为了测试这一点,我创建了一个简单的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
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后是代码

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));
}
Run Code Online (Sandbox Code Playgroud)

版画

compare(x) = false
compare(y) = false
compare(z) = true
Run Code Online (Sandbox Code Playgroud)

  • 从Rust 1.0开始,这个答案已经过时了.`FromPrimitive`特征已从[标准库中删除](https://github.com/rust-lang/rust/issues/16920). (2认同)