如果我们想比较某个类型的两个值 x 和 y 是否相等(不等),例如:x == y (x != y),那么我们就必须为类型实现 PartialEq Trait。
PartialEq 可使用 #[derive] 来交由编译器实现,当一个 struct 在进行相等比较时,会对其中每一个字段进行比较;如果遇到枚举时,还会对枚举所拥有的数据进行比较。
我们也可以自己实现 PartialEq,实现时只需要实现判断是否相等的函数 fn eq(&self, other: &Self) -> bool ,Rust 会自动提供 fn ne(&self, other: &Self) -> bool。
实现 Eq 的前提是已经实现了 PartialEq,因为实现 Eq 不需要额外的代码,只需要在实现了PartialEq 的基础上告诉编译器它的比较满足自反性就可以了。对于上面的例子只需要:#[derive(Eq)] 或 impl Eq for Book {}。
PartialEq 和 Eq
这两个 Traits 的名称实际上来自于抽象代数中的等价关系和局部等价关系。
等价关系(equivalence relation)即设 R 是某个集合 A 上的一个二元关系。若 R 满足以下条件:
- 自反性:∀x∈A, xRx
- 对称性:∀x,y∈A, xRy ⟹ yRx
- 传递性:xRz∀x,y,z∈A, (xRy ∧ yRz) ⟹ xRz
则称 R 是一个定义在 A 上的等价关系。
并非所有的二元关系都是等价关系, Eq 和 PartialEq 的区别在于是否在相等比较中是否满足自反性,即 x == x。
例如对于浮点类型,Rust 只实现了 PartialEq 而没有实现 Eq,原因在于 NaN != Nan,不满足自反性。
Eq 相比 PartialEq 需要额外满足自反性,即 a == a,对于浮点类型,Rust 只实现了 PartialEq 而不是 Eq,原因就是 NaN != NaN。
Eq 和 Hash
当一个类型同时实现了 Eq 和 Hash 时,该类型满足下列特性:
k1 == k2 -> hash(k1) == hash(k2)
即,当两个 key 相等时,它们的哈希值必然相等。Rust 里的 HashMap 和 HashSet 都依赖该特性。