部分可派生 Trait
等值比较 PartialEq 与 Eq
数学性质的区别
1. PartialEq (部分相等)
表示类型支持部分等价关系,满足以下性质:
- 对称性:若
a == b,则b == a; - 传递性:若
a == b且b == c,则a == c。 - 不要求自反性(即
a == a不一定成立)。 例如,浮点数中的NaN与自身比较返回false,因此f32/f64仅实现PartialEq。
2. Eq(完全相等)
PartialEq 的扩展,要求满足全等价关系,即额外满足以下性质:
- 自反性:
a == a必须为true。
只有所有值都能确定相等性的类型(如整数、字符串)才适合实现 Eq。
应用场景的差异
1. PartialEq 的典型用例
- 浮点数(
f32/f64):因NaN的存在无法满足自反性; - 自定义逻辑中部分字段参与比较(如仅比较
Book结构体的isbn字段); - 需要允许某些值无法比较的场景(如学生
id为 0 时禁止比较)。
2. Eq 的强制要求
- 当类型需要作为哈希容器(如
HashMap)的键时,必须实现Eq,因为哈希要求相等的键必须有相同的哈希值; - 需要严格全序比较的类型(如整数、字符串)。
实现方式与编译器支持
1. PartialEq 的实现
需手动定义 eq 方法(ne 有默认实现)。
示例:
|
|
若所有字段都参与比较,可通过 #[derive(PartialEq)] 自动派生。
2. Eq 的实现
仅需在 PartialEq 基础上添加空实现的标记 trait(impl Eq for Book {}),无额外逻辑。编译器通过标记确保类型满足自反性。
示例对比
1. 浮点数的实现
|
|
2. 自定义类型的选择
若某个结构体的 id 字段可能为 0(表示无效值),则仅实现 PartialEq,避免无效值的错误相等性判断。
总结
| 特性 | 数学要求 | 适用场景 | 编译器支持 |
|---|---|---|---|
PartialEq |
对称性、传递性 | 允许部分值不可比较(如 NaN) |
可派生或手动实现 |
Eq |
全等价关系(含自反性) | 哈希键、严格全序比较的类型 | 需手动标记,依赖 PartialEq 实现 |
次序比较 PartialOrd 与 Ord
数学性质的区别
1. PartialOrd(部分顺序)
表示类型支持部分顺序关系,满足以下性质:
- 自反性:
a <= a必须成立; - 反对称性:若
a <= b且b <= a,则a == b; - 传递性:若
a <= b且b <= c,则a <= c。 - 不要求所有值都可比较。例如,浮点数中的
NaN无法与任何值(包括自身)比较,因此f32/f64仅实现PartialOrd。
2. Ord(完全顺序)
PartialOrd 的扩展,要求满足全序关系,即额外满足以下性质:
- 所有值必须可比较,即任意两个值
a和b必须满足a <= b或b <= a。例如,整数类型(i32、u64等)必须实现Ord。
应用场景的差异
1. PartialOrd 的典型用例
- 浮点数:
NaN的存在导致无法全序比较; - 自定义类型存在不可比字段:如学生成绩可能因缺考而无法排序;
- 部分逻辑排序:如仅根据优先级字段排序,其他字段不参与。
2. Ord 的强制要求
- 类型需要作为排序容器(如
BTreeMap)的键时; - 需要稳定排序或哈希一致性(如
HashMap的键需实现Ord); - 需要严格全序的场景(如整数、字符串的字典序)。
实现方式与依赖关系
1. PartialOrd 的实现
- 依赖:必须已实现
PartialEq; - 方法:需定义
partial_cmp,返回Option<Ordering>(可能为None);
示例:
|
|
若所有字段参与比较,可通过 #[derive(PartialOrd)] 自动派生。
2. Ord 的实现
- 依赖:必须已实现
Eq和PartialOrd<Self>; - 方法:需定义
cmp,返回确定的Ordering;
示例:
|
|
手动实现需确保逻辑与 PartialOrd 一致。
示例对比
1. 浮点数的实现
|
|
2. 自定义类型的排序
|
|
总结
| 特性 | 数学要求 | 适用场景 | 实现依赖 |
|---|---|---|---|
PartialOrd |
部分顺序(允许不可比) | 浮点数、部分字段排序、不可比类型 | 需实现 PartialEq |
Ord |
全序(所有值可比) | 哈希键、稳定排序、严格全序类型 | 需实现 Eq + PartialOrd |
复制值 Clone 与 Copy
语义与行为差异
| 特性 | Copy | Clone |
|---|---|---|
| 复制方式 | 隐式按位复制(bitwise copy),浅拷贝 | 显式调用 clone(),可能是深拷贝 |
| 所有权影响 | 保留原变量所有权(无移动) | 保留原变量所有权,但需显式复制避免移动 |
| 数学性质 | 仅适用于简单值类型,满足按位复制的安全性 | 适用于任意类型,允许自定义复制逻辑 |
示例对比
|
|
实现约束与依赖
1. Copy 的实现条件
- 所有字段必须实现
Copy; - 类型不能包含析构函数(如
Droptrait); - 必须同时实现
Clone(自动派生时编译器处理)。
|
|
2. Clone 的灵活性
- 无字段类型限制,可包含堆数据(如
String、Vec); - 允许自定义深拷贝逻辑(如克隆时重置引用计数)。
|
|
性能与应用场景
| 场景 | Copy | Clone |
|---|---|---|
| 适用类型 | 基本类型(i32、f64)、简单结构体 |
堆分配类型(String、Vec)、复杂结构体 |
| 性能特征 | 零成本复制(栈内存操作) | 可能高开销(深拷贝堆数据) |
| 典型用途 | 高频复制的轻量级数据(如坐标、配置项) | 需要保留原对象的深拷贝场景 |
性能优化建议
- 小尺寸类型(如 <= 16 字节)优先考虑
Copy; - 避免为大型结构体实现
Copy(可能引发意外内存复制)。
总结
Copy:当数据简单且复制无副作用时(如基本类型)。Clone:当数据复杂或需显式控制复制行为时(如包含堆内存的类型)。