算术运算上的 Rust 泛型

rec*_*bbu 3 generics rust

我编写了一个通用函数来检查给定数字是否为偶数:

use std::ops::Rem;

fn main() {
    let x: u16 = 31;
    let y: u32 = 40;

    println!("x = {}, y = {}", is_even(x), is_even(y));
}

fn is_even<T: Rem<Output = T> + PartialEq>(n: T) -> bool {
    n % 2 == 0
}
Run Code Online (Sandbox Code Playgroud)

它会产生编译器错误:

use std::ops::Rem;

fn main() {
    let x: u16 = 31;
    let y: u32 = 40;

    println!("x = {}, y = {}", is_even(x), is_even(y));
}

fn is_even<T: Rem<Output = T> + PartialEq>(n: T) -> bool {
    n % 2 == 0
}
Run Code Online (Sandbox Code Playgroud)

T由于这是使用具体值(2 和 0)时的错误i32,我编写了另一个版本,is_even如下所示:

fn is_even<T: Rem<Output = T> + PartialEq> (n: T, with: T, output: T) -> bool {
    n % with == output
}
Run Code Online (Sandbox Code Playgroud)

这会产生所需的输出,但is_even现在使用起来很复杂:is_even(x, 2, 0)。如何制作我的原始版本is_even

我确实认识到模运算符足够通用,并且可以直接与u16u32值一起使用;is_even不需要像这样的函数- 但我编写它是为了理解泛型是如何工作的。

Pet*_*all 5

T您对自己做出的假设比您在类型中表达的要多得多。您已正确限制TRemand PartialEq,因此您可以使用%and==运算符,但您还假设存在特殊值02属于该类型。

没有存在 的特征2,但您可以使用crate 来查找具有 a和 anum-traits的类型的特征。假设泛型类型也具有加法,您可以通过向自身添加来确保它也具有加法。012one

extern crate num_traits;

use num_traits::{Zero, One};
use std::ops::{Rem, Add};

fn main() {
    let x: u16 = 31;
    let y: u32 = 40;

    println!("x = {}, y = {}", is_even(x), is_even(y));
}

fn is_even<T> (n: T) -> bool 
where
    T: Rem<Output = T> + PartialEq + One + Zero + Add + Copy
{
    let one: T = One::one();
    n % (one + one) == Zero::zero()
}
Run Code Online (Sandbox Code Playgroud)

请注意,我还做了T: Copy这样的操作,以便在加法表达式中不需要引用。对于大多数数字类型来说,这是一个合理的假设。


在不使用第三方 crate 的情况下,您还可以使用其他类型的值02例如u8,并且只需确保您的泛型类型可以从该类型转换而来。

fn is_even<T> (n: T) -> bool 
where
    T: Rem<Output = T> + PartialEq + From<u8>
{
    let two: T = 2u8.into();
    let zero: T = 0u8.into();
    n % two == zero
}
Run Code Online (Sandbox Code Playgroud)

我不太喜欢这个版本,因为约束T: From<u8>并没有真正传达有关该函数正在做什么的任何有用信息。它将类型与深奥的实现细节联系起来,而第一个版本的约束精确地描述了需要支持的算术运算。