Сравните аргумент общего числа с константой

Предположим, что у меня есть функция, которая сравнивает числовой аргумент с константой и возвращает логическое значение:

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

Как бы вы это сделали?

Ответ 1

Как вы видели, оператор Rust as очень ограничен в доступных бросках. В соответствии с Ручная инструкция,

Числовое значение может быть добавлено к любому числовому типу. Необработанное значение указателя может быть добавлено к типу целого типа или необработанного указателя. Любые другие роли не поддерживаются и не будут компилироваться.

Кроме того, Rust не выполняет какое-либо неявное числовое принуждение во время выполнения, поэтому вы должны явно принуждать аргументы операторов сравнения к одному и тому же типу (поскольку Ord определяет метод lt с прототипом fn lt(&self, other: &Self)).

Это вызывает интересную точку - к какому типу должны быть приведены аргументы оператору < в вашей функции compare, T или int (предполагаемый тип 42)? Похоже, в этом случае вы хотите сравнить n со значением 42 после преобразования в T. Самый простой способ добиться этого, оставаясь общим, - потребовать, чтобы T реализовал свойство FromPrimitive, содержащееся во внешнем num crate, который предоставляет методы получения значения типа T из 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));
}

печатает

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

Ответ 2

Вы можете попробовать это

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

выражение lhs > rhs равно < T как PartialOrd <U> :: gt (& lhs, & rhs)
где lhs имеет тип T, а rhs имеет тип U
Таким образом, T должен реализовать PartialOrd, чтобы разрешить использование оператора