Rust 內建 trait :PartialEq 和 Eq
阿新 • • 發佈:2021-03-28
> GitHub: https://github.com/storagezhang
>
> Emai: [email protected]
>
> 華為雲社群: https://bbs.huaweicloud.com/blogs/250275
> [Eq](https://doc.rust-lang.org/std/cmp/trait.Eq.html) and [PartialEq](https://doc.rust-lang.org/std/cmp/trait.PartialEq.html) are traits that allow you to define total and partial equality between values, respectively. Implementing them overloads the == and != operators.
# PartialEq
```rust
/// [`eq`]: PartialEq::eq
/// [`ne`]: PartialEq::ne
#[lang = "eq"]
#[stable(feature = "rust1", since = "1.0.0")]
#[doc(alias = "==")]
#[doc(alias = "!=")]
#[rustc_on_unimplemented(
message = "can't compare `{Self}` with `{Rhs}`",
label = "no implementation for `{Self} == {Rhs}`"
)]
pub trait PartialEq {
/// This method tests for `self` and `other` values to be equal, and is used
/// by `==`.
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
fn eq(&self, other: &Rhs) -> bool;
/// This method tests for `!=`.
#[inline]
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
fn ne(&self, other: &Rhs) -> bool {
!self.eq(other)
}
}
```
如果我們想比較某個型別的兩個值 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`。例子如下:
```rust
enum BookFormat {
Paperback,
Hardback,
Ebook,
}
struct Book {
isbn: i32,
format: BookFormat,
}
impl PartialEq for Book {
fn eq(&self, other: &Self) -> bool {
self.isbn == other.isbn
}
}
```
# Eq
```rust
pub trait Eq: PartialEq {
// this method is used solely by #[deriving] to assert
// that every component of a type implements #[deriving]
// itself, the current deriving infrastructure means doing this
// assertion without using a method on this trait is nearly
// impossible.
//
// This should never be implemented by hand.
#[doc(hidden)]
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
fn assert_receiver_is_total_eq(&self) {}
}
```
實現 `Eq` 的前提是已經實現了 `PartialEq`,因為實現 `Eq` 不需要額外的程式碼,只需要在實現了`PartialEq` 的基礎上告訴編譯器它的比較滿足自反性就可以了。對於上面的例子只需要:`#[derive(Eq)]` 或 `impl Eq for Book {}`。
```rust
enum BookFormat {
Paperback,
Hardback,
Ebook,
}
struct Book {
isbn: i32,
format: BookFormat,
}
impl PartialEq for Book {
fn eq(&self, other: &Self) -> bool {
self.isbn == other.isbn
}
}
impl Eq for Book {}
```
# PartialEq 和 Eq
這兩個 Traits 的名稱實際上來自於抽象代數中的等價關係和區域性等價關係。
**等價關係**(equivalence relation)即設 $\displaystyle R$ 是某個集合 $\displaystyle A$ 上的一個二元關係。若 $\displaystyle R$ 滿足以下條件:
1. 自反性:$\displaystyle \forall x\in A,~~xRx$
2. 對稱性:$\displaystyle \forall x,y\in A,~~xRy~~\implies ~~yRx$
3. 傳遞性:$\displaystyle \forall x,y,z\in A,~~~(xRy~~\wedge ~~yRz)~~\implies ~~xRz$
則稱 $\displaystyle R$ 是一個定義在 $\displaystyle A$ 上的**等價關係**。
並非所有的二元關係都是等價關係, `Eq` 和 `PartialEq` 的區別在於是否在相等比較中是否滿足自反性,即 `x == x`。
例如對於浮點型別,Rust 只實現了 `PartialEq` 而沒有實現 `Eq`,原因在於 `NaN != Nan`,不滿足自反性。
`Eq` 相比 `PartialEq` 需要額外滿足自反性,即 `a == a`,對於浮點型別,Rust 只實現了 PartialEq 而不是 Eq,原因就是 `NaN != NaN`。
# Eq 和 Hash
當一個型別同時實現了 `Eq` 和 `Hash` 時,該型別滿足下列特性:
```text
k1 == k2 -> hash(k1) == hash(k2)
```
即,當兩個 key 相等時,它們的雜湊值必然相等。Rust 裡的 `HashMap` 和 `HashSet` 都依賴該