在Rust中,PartialEq和Eq特性源自类型安全的理念——所有对集合的基本操作(HashMap,HashSet)都基于严格的等式定义。这些特性定义了两个类型的对象是否可以通过==进行比较,以及类型是否有义务实现“完全”的等式(Eq)或“部分”的等式(PartialEq)。
错误地实现PartialEq/Eq会导致集合的错误工作,违反基本的数学性质:与自身的比较、对称性、传递性。此外,错误的Eq可能导致对象在集合中“丢失”或出现意外的重复项。
PartialEq适用于某些值无法比较的类型(例如,f32,其中NaN != NaN)。Eq仅适用于“严格的”(全域的)关系。用户类型必须根据这些契约严格实现比较。
示例代码:
#[derive(PartialEq, Eq)] struct Point { x: i32, y: i32, } let p1 = Point { x: 1, y: 2 }; let p2 = Point { x: 1, y: 2 }; assert!(p1 == p2); // true
关键特点:
为什么对于f32类型,标准库仅实现PartialEq,而不实现Eq?
因为根据IEEE-754规范,NaN != NaN,即x == x的属性对于所有x不成立,而这对于Eq是必要的属性。
如果手动实现了PartialEq,是否必须显式实现Eq?
是的,如果该类型支持完全等式(x == x始终为true),则必须手动实现Eq,否则一些结构将无法与您的类型编译。
用户是否可以“非对称”地实现PartialEq(x == y,但y != x)?
从技术上讲,可以,但这会导致集合错误工作,并被视为bug。
**负面案例
用户为一种仅根据一个字段进行比较的类型实现PartialEq,忽略了其余字段。结果HashSet“丢失”了重复项。
优点:
缺点:
**积极案例
使用#[derive(PartialEq, Eq)]或实现自己版本的比较,考虑到所有业务逻辑,确保所需的属性。
优点:
缺点: