我一直在线阅读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>
)?它只是一个惯例,还是有特定的理由呢?
答案 0 :(得分:9)
当函数对变量进行操作时,您可以将特征视为对类型进行操作的函数。当想到这种方式时,类型参数用作函数的输入,相关类型用作输出。
由于Output
是关联类型,对于我们希望A
的两种B
和impl Add
类型,我们只能选择一个Output
类型。如果是Output
类型参数,我们可以impl Add
A
和B
以无数种方式{。}}。
例如,让我们定义一个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
}
}
答案 1 :(得分:4)
关于谁来指定输出类型。通常,当您将f64
乘以f64
两种类型时,它会明确结果的类型:f64
。调用者要求String
的结果类型是没有意义的 - 所以它作为输入并不真正有意义。
我可以想象一些您可能希望选择不同输出的情况,但它们有点利基(比如u32
* u32
返回完整的u64
结果?) ;加上如果有多个选项,你如何指定你想要的?虽然您可以编写Add<u32, u64>::add(a, b)
,但它会使a*b*c
之类的简单表达式变得模糊,并使类型推断变得更加困难!