当我们只有一个通用特征时,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
作为二.
这是我损坏的示例代码。请注意,我只想将值加倍并允许任何知道如何相乘的东西。
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
作为二.