1. 核心定义与区别
PartialEq:部分等价关系
- 定义:允许某些值不与自身相等。
- 核心方法 :
fn eq(&self, other: &Rhs) -> bool和fn ne(&self, other: &Rhs) -> bool。 - 典型场景 :浮点数 (
f32,f64)。因为NaN != NaN,所以浮点数只能实现PartialEq,不能实现Eq。f64::NAN != f64::NAN - 数学性质 :满足对称性(若 ab 则 ba)和传递性(若 ab 且 bc 则 ac),但不要求自反性(aa 不一定成立)。
Eq:完全等价关系
- 定义 :标记 Trait(Marker Trait),没有方法。它依赖于
PartialEq,并承诺满足自反性。 - 核心约束 :对于所有
a,a == a必须返回true。 - 典型场景 :整数、字符串、布尔值、结构体(如果其所有字段都实现了
Eq)。 - 作用 :作为类型系统的约束,确保该类型可以用于需要严格相等性的场景(如
HashMap的键、BTreeSet等)。
HashMap 的键必须实现 Eq 以及 Hash,在编译期就阻止了使用 f32/f64 作为键,从而避免了这类运行时 bug。
2、自定义结构体派生PartialEq、 Eq
rust
use std::collections::HashMap;
// 子类型有浮点类型不能派生 Eq
// 因为 f32 没有实现 Eq
#[derive(Debug, PartialEq)]
struct Point {
x: f32,
y: f32,
}
#[derive(Debug, PartialEq, Eq, Hash)]
struct User {
name: String, // String 实现了 Eq
age: i32, // i32 实现了 Eq
}
fn main() {
let p1 = Point { x: 1.2, y: 5.6 };
let p2 = Point { x: 1.2, y: 5.6 };
println!("{}", p1 == p2); // true
let u1 = User { name: String::from("Jane"), age: 18 };
let u2 = User { name: String::from("Jane"), age: 18 };
println!("{}", u1 == u2); // true
let mut hm = HashMap::new();
hm.insert(u1, "Jjjj"); // 实现了PartialEq, Eq, Hash,可以做键
println!("{:?}", hm.get(&u2)); // Some("Jjjj")
}
3、自定义类型手动实现PartialEq、Eq
rust
// 自定义类型
struct MyStr(String);
// 实现 PartialEq
impl PartialEq for MyStr {
// 实现 eq 方法
fn eq(&self, other: &Self) -> bool {
self.0.to_lowercase() == other.0.to_lowercase() // 转换为小写
}
}
// 实现 Eq
// Eq 是一个标记 Trait,没有任何方法
// 只需写下面这句代码
impl Eq for MyStr {}
fn main() {
let ms1 = MyStr(String::from("Hello"));
let ms2 = MyStr(String::from("heLLo"));
println!("{}", ms1 == ms2); // true,忽略大小写进行比较
}
ne 方法(不等于)
- 通常不需要手动实现
ne(不等于)。PartialEqtrait 提供了默认的ne实现:
rust
fn ne(&self, other: &Rhs) -> bool {
!self.eq(other)
}
除非你有性能优化的特殊需求(例如可以直接判断不相等而无需完整比较),否则省略它即可。
4、自定义结构体手动实现PartialEq、Eq,只比较部分属性
rust
// 自定义类型
struct MyStruct {
name: String,
age: u8,
time: u64,
}
// 实现 PartialEq
impl PartialEq for MyStruct {
fn eq(&self, other: &Self) -> bool {
self.name == other.name && self.age == other.age // 不比较time
}
}
// 实现 Eq
impl Eq for MyStruct {}
fn main() {
let ms1 = MyStruct { name: String::from("小刚"), age: 18, time: 33 };
let ms2 = MyStruct { name: String::from("小刚"), age: 18, time: 65 };
println!("{}", ms1 == ms2); // true
}