部分可派生 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
; - 类型不能包含析构函数(如
Drop
trait); - 必须同时实现
Clone
(自动派生时编译器处理)。
|
|
2. Clone 的灵活性
- 无字段类型限制,可包含堆数据(如
String
、Vec
); - 允许自定义深拷贝逻辑(如克隆时重置引用计数)。
|
|
性能与应用场景
场景 | Copy | Clone |
---|---|---|
适用类型 | 基本类型(i32 、f64 )、简单结构体 |
堆分配类型(String 、Vec )、复杂结构体 |
性能特征 | 零成本复制(栈内存操作) | 可能高开销(深拷贝堆数据) |
典型用途 | 高频复制的轻量级数据(如坐标、配置项) | 需要保留原对象的深拷贝场景 |
性能优化建议
- 小尺寸类型(如 <= 16 字节)优先考虑
Copy
; - 避免为大型结构体实现
Copy
(可能引发意外内存复制)。
总结
Copy
:当数据简单且复制无副作用时(如基本类型)。-
Clone
:当数据复杂或需显式控制复制行为时(如包含堆内存的类型)。