运算符重载
既然我们对特质(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
目录下。