为什么Rust的运算符具有Output变量类型?

时间:2016-08-24 05:51:27

标签: generics operator-overloading rust

我一直在线阅读Rust书,我已经到了4.1, Operators and Overloading。我注意到std::ops::Add将其定义为fn add(self, rhs: RHS) -> Self::Output;,并在特征中单独定义了type Output

我理解这里发生了什么:add函数接受左侧为self,右侧接受通用参数类型RHS

我想知道为什么输出类型是用Output别名定义的,而不是另一个通用的(例如Add<RHS, Output>)?它只是一个惯例,还是有特定的理由呢?

2 个答案:

答案 0 :(得分:9)

当函数对变量进行操作时,您可以将特征视为对类型进行操作的函数。当想到这种方式时,类型参数用作函数的输入,相关类型用作输出。

由于Output是关联类型,对于我们希望A的两种Bimpl Add类型,我们只能选择一个Output类型。如果是Output类型参数,我们可以impl Add AB以无数种方式{。}}。

例如,让我们定义一个Mul特征,其中Output是一个参数:

trait Mul<RHS, Output> {
    fn mul(self, rhs: RHS) -> Output;
}

现在让我们定义一个Complex类型:

#[derive(Debug, Clone, Copy)]
struct Complex {
    x: f64,
    y: f64,
}

impl Complex {
    fn new(x: f64, y: f64) -> Complex {
        Complex { x: x, y: y }
    }
}

我们希望能够将其乘以f64

impl Mul<f64, Complex> for Complex {
    fn mul(self, rhs: f64) -> Complex {
        Complex::new(self.x * rhs, self.y * rhs)
    }
}

一切正常。但是,我们可以提出第二个实现:

impl Mul<f64, f64> for Complex {
    fn mul(self, rhs: f64) -> f64 {
        self.x * rhs
    }
}

当我们现在将Complex乘以f64时,应该调用哪个实现是不明确的,并且调用者需要提供额外的类型信息。通过使Output成为关联类型,这是不允许的。以下代码会针对冲突的实现引发编译器错误:

impl std::ops::Mul<f64> for Complex {
    type Output = Complex;
    fn mul(self, rhs: f64) -> Complex {
        Complex::new(self.x * rhs, self.y * rhs)
    }
}

impl std::ops::Mul<f64> for Complex {
    type Output = f64;
    fn mul(self, rhs: f64) -> Complex {
        self.x * rhs
    }
}

Full example

答案 1 :(得分:4)

关于谁来指定输出类型。通常,当您将f64乘以f64两种类型时,它会明确结果的类型:f64。调用者要求String的结果类型是没有意义的 - 所以它作为输入并不真正有意义。

我可以想象一些您可能希望选择不同输出的情况,但它们有点利基(比如u32 * u32返回完整的u64结果?) ;加上如果有多个选项,你如何指定你想要的?虽然您可以编写Add<u32, u64>::add(a, b),但它会使a*b*c之类的简单表达式变得模糊,并使类型推断变得更加困难!