当我们只有一个通用特征时,Rust 处理常量数学的方法是什么

What is the Rust way to deal with constant math when we only have a generic trait

这是我损坏的示例代码。请注意,我只想将值加倍并允许任何知道如何相乘的东西。

fn by_two<T: Mul<Output = T>>(x: T) -> T {
    x * 2
}

我认为您 运行 遇到的问题是您需要说明乘以什么。 2 默认为 <integer>,如果未指定则视为 i32,所以这里是示例:

fn by_two<T: Mul<i32, Output=T>>(x: T) -> T {
    x * 2
}

或者,您可以将结果留给特征。这通常*等同于只使用 x * 2 而不调用函数。

fn by_two<T: Mul<i32>>(x: T) -> <T as Mul<i32>>::Output {
    x * 2
}

然而,这不适用于大多数类型,因为 Rust 通常期望基元与相同类型的其他私有类型相乘。我们可以将其更改为使用 From<i32> 以使其更具包容性。

fn by_two<T: Mul + From<i32>>(x: T) -> <T as Mul>::Output {
    x * T::from(2)
}

此时您可能会注意到问题所在。我们需要弄清楚“2”是什么类型,以便它可以与 T 相乘。 From<i32> 解决方案适用于原始类型,但如果您尝试将矢量或矩阵插入函数,您将开始 运行 遇到困难。一种解决方案是添加一个单独的泛型,以允许用户指定它乘以什么。

fn by_two<T: Mul<K>, K: From<i32>>(x: T) -> <T as Mul<K>>::Output {
    x * K::from(2)
}

但是,这开始变得有点麻烦了。与以前的版本不同,我们不能让编译器推断用于 K 的类型,因为可能有多种类型可以满足 K.

的要求

一个选项虽然不能完全解决我们的问题,但可能对我们有一点帮助,那就是使用像 num-traits 这样的板条箱。老实说,这实际上与示例 2 大致相同,但具有更笼统的概念 2。

use num_traits::identities::One;
fn by_two<T: One + Add<Output=T>>(x: T) -> T {
    x * (T::one() + T::one())
}

您有很多选择,但 none 无所不包。如果你想让你的匹配尽可能通用,你可能最好先编写函数,然后确定值必须满足哪些数学属性才能使其工作。您可以使用 num-traits 作为基本特征界限,例如区分浮点数和整数。然后对于更复杂的概念,我建议使用 alga.

嗯……两个?例如,如果我将 Color 上的乘法定义为混合两种颜色,那么将一种颜色乘以二是什么意思?

解决这个问题的最简单方法是选择一个非常小的数据类型,两个适合(例如 u8)并接受任何 T 可以(尝试)从 u8:

fn by_two<T: Mul<Output = T> + TryFrom<u8>>(x: T) -> T {
    x * 2.try_into().ok().expect("could not convert two into T")
}

更通用(但麻烦)的方法是使用num crate,并接受任何可以相乘的东西,并且有一个One的概念,使用One + One作为二.