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


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

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

你会怎么做到这一点?


3500
2018-01-12 10:43


起源

我不是专家,但据我所知,你不能在Rust中做20> 10.5。这意味着你不能比较两种不同的类型。 - sunny1304
正确。我希望有一种方法可以自动将常量转换为兼容类型。常量应该在Go中首先应该是无类型的。 - Gunchars


答案:


正如你所见,Rust as 操作员在它允许的演员表中非常有限。根据 铁锈手册

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

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

这引出了一个有趣的观点 - 论者的论据应该是哪种类型 < 你的经营者 compare 功能被投射, T 要么 int (假定的类型 42)?在这种情况下,您似乎想要进行比较 n 价值 42 转换成后 T。在保持通用性的同时实现这一目标的最直接的方法是要求 T 实施 FromPrimitive 特质,包含在外部 num 箱,它提供了获取类型值的方法 T 从一个 int (或其他Rust原始数字类型)。你的 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));
}

版画

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

9
2018-01-12 22:57



我没有生锈经验,但完整性+1 - Bohemian♦
从Rust 1.0开始,这个答案已经过时了。该 FromPrimitive 特质已经 从标准库中删除。 - Craig M. Brandenburg


你可以试试这个

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

表达 lhs> rhs 等于 <T as PartialOrd <U >> :: gt(&lhs,&rhs)
哪里 LHS 有类型 Ť 和 RHS 有类型 ü
从而 Ť 必须实施 PartialOrd 为了允许使用操作员 >


2
2017-08-04 21:09



这个答案出现在低质量的审查队列中,大概是因为你没有解释代码。如果你解释它(在你的答案中),你更有可能得到更多的赞成 - 而提问者更有可能学到一些东西! - The Guy with The Hat
但是唯一的T:PartialOrd <i32> STD 是i32 - Jacob