如何要求泛型类型在泛型函数中实现 Add、Sub、Mul 或 Div 等操作?
How do I require a generic type implement an operation like Add, Sub, Mul, or Div in a generic function?
我正在尝试在 Rust 中实现一个通用函数,其中对参数的唯一要求是应该定义乘法运算。我正在尝试实现通用 "power",但会使用更简单的 cube
函数来说明问题:
use std::ops::Mul;
fn cube<T: Mul>(x: T) -> T {
x * x * x
}
fn main() {
println!("5^3 = {}", cube(5));
}
编译时出现此错误:
error[E0369]: binary operation `*` cannot be applied to type `<T as std::ops::Mul>::Output`
--> src/main.rs:4:5
|
4 | x * x * x
| ^^^^^^^^^
|
= note: an implementation of `std::ops::Mul` might be missing for `<T as std::ops::Mul>::Output`
这是什么意思?我选错了特质吗?我该如何解决?
让我们稍微分解一下您的示例:
fn cube<T: Mul>(x: T) -> T {
let a = x * x;
let b = a * x;
b
}
a
和b
的类型是什么?在这种情况下,a
的类型是 <T as std::ops::Mul>::Output
— 从错误消息中听起来很熟悉?然后,我们再次尝试将该类型乘以 x
,但不能保证 Output
能够乘以任何东西!
让我们做最简单的事情,假设 T * T
需要得到 T
:
fn cube<T: Mul<Output = T>>(x: T) -> T {
x * x * x
}
不幸的是,这给出了两个类似的错误:
error[E0382]: use of moved value: `x`
--> src/lib.rs:6:9
|
6 | x * x * x
| - ^ value used here after move
| |
| value moved here
|
= note: move occurs because `x` has type `T`, which does not implement the `Copy` trait
这是因为 Mul
trait takes arguments by value,所以我们添加 Copy
这样我们就可以复制值。
我也切换到 where
子句,因为我更喜欢它,而且有那么多内联是笨拙的:
fn cube<T>(x: T) -> T
where
T: Mul<Output = T> + Copy
{
x * x * x
}
另请参阅:
绑定T: Mul
并不意味着二元运算符的结果也是T
类型。结果类型是此特征的关联类型:Output
.
另一个问题是,在 Rust 1.0 之前,运算符特征从按引用传递转换为按值传递。在通用代码中,这可能有点麻烦(至少现在是这样),因为这些运算符 消耗 它们的操作数,除非您还要求类型为 Copy
。
为了完整起见(如果您不想要求 Copy
),让我添加一些关于可能的替代方向的信息。
为了通用代码,鼓励 "numeric types" 的作者提供额外的 非消耗性 实现这些运算符特征,这样您就不需要 Copy
或 Clone
。例如,标准库已经提供了以下实现:
f64 implements Mul< f64>
f64 implements Mul<&f64>
&f64 implements Mul< f64>
&f64 implements Mul<&f64>
这些实现中的每一个都有 f64
作为 Output
类型。直接利用这些特征并不漂亮:
fn cube<T>(x: &T) -> T
where
for<'a> T: Mul<&'a T, Output = T>,
for<'a, 'b> &'a T: Mul<&'b T, Output = T>,
{
x * x * x
}
最终,我们可能会获得一些(稍微)更高级别的特征,这将减少噪音。例如:T: Mul2
可能意味着 T: Mul<T> + Mul<&T>
和 &T: Mul<T> + Mul<&T>
,但在撰写本文时,Rust 编译器似乎无法处理此问题。至少我无法成功编译以下代码:
use std::ops::Mul;
pub trait Mul2
where
Self: Mul<Self, Output = Self>,
Self: for<'a> Mul<&'a Self, Output = Self>,
for<'a> &'a Self: Mul<Self, Output = Self>,
for<'a, 'b> &'a Self: Mul<&'b Self, Output = Self>,
{
}
impl<T> Mul2 for T
where
T: Mul<T, Output = T>,
T: for<'a> Mul<&'a T, Output = T>,
for<'a> &'a T: Mul<T, Output = T>,
for<'a, 'b> &'a T: Mul<&'b T, Output = T>,
{
}
fn cube<T: Mul2>(x: &T) -> T {
x * x * x
}
fn main() {
let c = cube(&2.3);
println!("Hello, world! {}", c)
}
我认为可以肯定地说,这方面的情况会有所改善。目前,在 Rust 中通用实现数值算法的能力并不像我希望的那样好。
我正在尝试在 Rust 中实现一个通用函数,其中对参数的唯一要求是应该定义乘法运算。我正在尝试实现通用 "power",但会使用更简单的 cube
函数来说明问题:
use std::ops::Mul;
fn cube<T: Mul>(x: T) -> T {
x * x * x
}
fn main() {
println!("5^3 = {}", cube(5));
}
编译时出现此错误:
error[E0369]: binary operation `*` cannot be applied to type `<T as std::ops::Mul>::Output`
--> src/main.rs:4:5
|
4 | x * x * x
| ^^^^^^^^^
|
= note: an implementation of `std::ops::Mul` might be missing for `<T as std::ops::Mul>::Output`
这是什么意思?我选错了特质吗?我该如何解决?
让我们稍微分解一下您的示例:
fn cube<T: Mul>(x: T) -> T {
let a = x * x;
let b = a * x;
b
}
a
和b
的类型是什么?在这种情况下,a
的类型是 <T as std::ops::Mul>::Output
— 从错误消息中听起来很熟悉?然后,我们再次尝试将该类型乘以 x
,但不能保证 Output
能够乘以任何东西!
让我们做最简单的事情,假设 T * T
需要得到 T
:
fn cube<T: Mul<Output = T>>(x: T) -> T {
x * x * x
}
不幸的是,这给出了两个类似的错误:
error[E0382]: use of moved value: `x`
--> src/lib.rs:6:9
|
6 | x * x * x
| - ^ value used here after move
| |
| value moved here
|
= note: move occurs because `x` has type `T`, which does not implement the `Copy` trait
这是因为 Mul
trait takes arguments by value,所以我们添加 Copy
这样我们就可以复制值。
我也切换到 where
子句,因为我更喜欢它,而且有那么多内联是笨拙的:
fn cube<T>(x: T) -> T
where
T: Mul<Output = T> + Copy
{
x * x * x
}
另请参阅:
绑定T: Mul
并不意味着二元运算符的结果也是T
类型。结果类型是此特征的关联类型:Output
.
另一个问题是,在 Rust 1.0 之前,运算符特征从按引用传递转换为按值传递。在通用代码中,这可能有点麻烦(至少现在是这样),因为这些运算符 消耗 它们的操作数,除非您还要求类型为 Copy
。
为了完整起见(如果您不想要求 Copy
),让我添加一些关于可能的替代方向的信息。
为了通用代码,鼓励 "numeric types" 的作者提供额外的 非消耗性 实现这些运算符特征,这样您就不需要 Copy
或 Clone
。例如,标准库已经提供了以下实现:
f64 implements Mul< f64>
f64 implements Mul<&f64>
&f64 implements Mul< f64>
&f64 implements Mul<&f64>
这些实现中的每一个都有 f64
作为 Output
类型。直接利用这些特征并不漂亮:
fn cube<T>(x: &T) -> T
where
for<'a> T: Mul<&'a T, Output = T>,
for<'a, 'b> &'a T: Mul<&'b T, Output = T>,
{
x * x * x
}
最终,我们可能会获得一些(稍微)更高级别的特征,这将减少噪音。例如:T: Mul2
可能意味着 T: Mul<T> + Mul<&T>
和 &T: Mul<T> + Mul<&T>
,但在撰写本文时,Rust 编译器似乎无法处理此问题。至少我无法成功编译以下代码:
use std::ops::Mul;
pub trait Mul2
where
Self: Mul<Self, Output = Self>,
Self: for<'a> Mul<&'a Self, Output = Self>,
for<'a> &'a Self: Mul<Self, Output = Self>,
for<'a, 'b> &'a Self: Mul<&'b Self, Output = Self>,
{
}
impl<T> Mul2 for T
where
T: Mul<T, Output = T>,
T: for<'a> Mul<&'a T, Output = T>,
for<'a> &'a T: Mul<T, Output = T>,
for<'a, 'b> &'a T: Mul<&'b T, Output = T>,
{
}
fn cube<T: Mul2>(x: &T) -> T {
x * x * x
}
fn main() {
let c = cube(&2.3);
println!("Hello, world! {}", c)
}
我认为可以肯定地说,这方面的情况会有所改善。目前,在 Rust 中通用实现数值算法的能力并不像我希望的那样好。