编程库开发者

在Rust中,PartialEq和Eq是什么,它们在比较值时提供了什么契约,以及为什么在用户类型中遵循严格等式很重要?

用 Hintsage AI 助手通过面试

答案。

问题的背景

在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

关键特点:

  • PartialEq允许“部分”比较——可能存在无法正确比较的值(例如,NaN)。
  • Eq要求完全关系:x == x必须始终为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的实现“有缺陷”。
  • 对于可能的类型未实现Eq,并破坏了HashMap/HashSet的工作。
  • 按“自己”的方式实现对称性,违反了规范。

现实生活中的例子

**负面案例

用户为一种仅根据一个字段进行比较的类型实现PartialEq,忽略了其余字段。结果HashSet“丢失”了重复项。

优点:

  • 实现简单,可以快速比较。

缺点:

  • 错误匹配,失去唯一性。

**积极案例

使用#[derive(PartialEq, Eq)]或实现自己版本的比较,考虑到所有业务逻辑,确保所需的属性。

优点:

  • 集合正常工作,行为严格。

缺点:

  • 设计时需要仔细和深入的思考。