运算符重载
既然我们对特质(traits)有了基本的认识,现在让我们回过头来探讨一下运算符重载(operator overloading)。
运算符重载是指为诸如+、-、*、/、==、!=等运算符定义自定义行为的能力。
在Rust中,运算符是通过特质来实现的。对于每个运算符,都存在一个相应的特质来定义该运算符的行为。通过为你的类型实现这些特质,你就能解锁使用对应的运算符。
例如,PartialEq特质定义了==和!=这两个运算符的行为:
#![allow(unused)] fn main() { // 从Rust标准库中简化得来的`PartialEq`特质定义 pub trait PartialEq { // 必须实现的方法 // `Self`是一个Rust关键字,表示“实现该特质的类型” fn eq(&self, other: &Self) -> bool; // 默认提供的方法 fn ne(&self, other: &Self) -> bool { ... } } }
当你编写x == y时,编译器会查找x和y类型的PartialEq特质的实现,并将x == y替换为x.eq(y)。这是一种语法糖!
以下是主要运算符与其对应特质的对照表:
算术运算符位于std::ops模块中,而比较运算符则位于std::cmp模块。
默认实现
关于PartialEq::ne的注释说明它是“提供的方法”。这意味着PartialEq在其特质定义中为ne提供了一个默认实现——即定义片段中的省略号{ ... }所代表的部分。如果展开这部分,它看起来是这样的:
#![allow(unused)] fn main() { pub trait PartialEq { fn eq(&self, other: &Self) -> bool; fn ne(&self, other: &Self) -> bool { !self.eq(other) } } }
这正如你所料:ne是eq的否定。由于提供了默认实现,当你为自己的类型实现PartialEq时,可以省略实现ne。实现eq就足够了:
#![allow(unused)] fn main() { struct WrappingU8 { inner: u8, } impl PartialEq for WrappingU8 { fn eq(&self, other: &WrappingU8) -> bool { self.inner == other.inner } // 这里没有`ne`的实现 } }
然而,你并不一定要使用默认实现。在实现特质时,你可以选择覆盖它:
#![allow(unused)] fn main() { struct MyType; impl PartialEq for MyType { fn eq(&self, other: &MyType) -> bool { // 自定义实现 } fn ne(&self, other: &MyType) -> bool { // 自定义实现 } } }
参考练习
- 本节的练习位于
exercises/04_traits/03_operator_overloading目录下。