如何在泛型函数中要求泛型类型实现Add,Sub,Mul或Div等操作?

时间:2015-03-21 15:18:36

标签: generics 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`

这是什么意思?我选择了错误的特质吗?我该如何解决这个问题?

2 个答案:

答案 0 :(得分:28)

让我们稍微分解你的例子:

fn cube<T: Mul>(x: T) -> T {
    let a = x * x;
    let b = a * x;
    b
}

ab有哪些类型?在这种情况下,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
}

另见:

答案 1 :(得分:12)

绑定T: Mul并不意味着二元运算符的结果也是T类型。结果类型是此特征的关联类型Output

另一个问题是在Rust 1.0之前,运算符traits从pass-by-reference切换到pass-by-value。在通用代码中,这可能是一个痛苦的屁股(至少现在),因为这些运算符消耗他们的操作数,除非你还要求类型为Copy

为了完整性(如果您不想要Copy),请允许我添加一些可能的替代方向的信息。

为了通用代码,&#34;数字类型的作者&#34;我们鼓励您提供这些运营商特征的其他非消费实施,以便您不需要CopyClone。例如,标准库已提供以下实现:

 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中一般实现数值算法的能力并不像我希望的那样好。