感谢chaseSpace的投稿,原文链接:https://github.com/chaseSpace/rust_practices/blob/main/blogs/about_eq_ord_trait.md
前言
1. 数学中的相等关系
1.1 部分相等关系
1.2 部分相等与全相等的关系
1.3 小结
2. 编程与数学的关系
3. PartialEq
3.1 trait定义
3.2 对应操作符
3.3 可派生
3.4 手动实现PartialEq
3.5 比较不同的类型
3.6 Rust基本类型如何实现PartialEq
4. Eq
4.1 trait定义
4.2 对应操作符
4.3 可派生
4.4 手动实现Eq
4.5 比较不同的类型
4.6 Rust基本类型如何实现Eq
5. 对浮点数的测试
6. PartialOrd和Ord
6.1 与PartialEq和Eq的关系
6.2 基本性质
6.3 trait定义
6.4 可派生
6.5 手动实现PartialOrd和Ord
6.6 比较不同的类型
6.7 Rust基本类型如何实现PartialOrd和Ord
6.8 为其他类型实现四大compare-trait
前言
本文将围绕对象:PartialEq和Eq,以及PartialOrd和Ord,即四个Rust中重点的Compare Trait进行讨论并解释其中的细节,内容涵盖理论以及代码实现。
在正式介绍PartialEq和Eq、以及PartialOrd和Ord之前,本文会首先介绍它们所遵循的数学理论,也就是相等关系。文章主要分三大部分,第一部分是第1节,讨论的是数学中的相等关系;第二部分是第2~5节,主要讨论PartialEq和Eq;第三部分是第6节,主要讨论PartialOrd和Ord。内容描述可能具有先后顺序,建议按章节顺序阅读。
声明
本文内容来自作者个人的学习成果总结及整理,可能会存在因个人水平导致的表述错误,欢迎并感谢读者指正!
作者:Leigg
首发地址:https://github.com/chaseSpace/rust_practices/blob/main/blogs/about_eq_ord_trait.md
CSDN:https://blog.csdn.net/sc_lilei/article/details/129322616
发布时间:2023年03月03日
License:CC-BY-NC-SA 4.0 (转载请注明作者及来源)
1. 数学中的相等关系
在初中数学中,会介绍到什么是相等关系(也叫等价关系),相等关系是一种基本的二元关系,它描述了两个对象之间的相等性质。它必须满足如下三个性质:
自反性(反身性):自己一定等于自己,即
a=a
;对称性:若有
a=b
,则有b=a
;传递性:若有
a=b
和b=c
,则有a=c
;
也就是说,满足这三个性质才叫满足(完全)相等关系。这很容易理解,就不过多解释。
1.1 部分相等关系
对于简单的整数类型、字符串类型,我们可以说它们具有完全相等关系,因为它们可以全方位比较(包含两个维度,第一个是类型空间中的任意值,第二个是每个值的任意成员属性), 但是对于某些类型就不行了,这些类型总是不满足其中一个维度 。下面一起来看看:
以字符串为例,全方位比较的是它的每个字节值以及整个字符串的长度。
0. 浮点数类型
在浮点数类型中有个特殊的值是NaN(Not-a-number),这个值与任何值都不等(包括自己),它直接违背了自反性。这个时候,我们就需要为浮点数定义一种部分相等关系,这主要是为了比较非NaN浮点数。
NaN定义于IEEE 754-2008标准的5.2节“特殊值”(Special Values)中,除了NaN,另外两个特殊值是正无穷大(+infinity)、负无穷大(-infinity),不过这两个值满足自反性。
除了浮点数类型,数学中还有其他类型也不具有通常意义上的全等关系,比如集合类型、函数类型。
1. 集合类型
假设有集合A={1,2,3}、B={1,3,2},那么此时A和B是相等还是不相等呢?这就需要在不同角度去看待,当我们只关注集合中是否包含相同的元素时, 可以说它们相等,当我们还要严格要求元素顺序一致时,它们就不相等。
在实际应用中,由我们定义(Impl)了一种集合中的特殊相等关系,称为"集合的相等",这个特殊关系(实现逻辑)中,我们只要求两个集合的元素相同,不要求其他。
2. 函数类型
首先从浮点数的NaN角度来看函数,假设有函数A=f(x)、B=f(y),若x=y,那显然A的值也等于B,但是如果存在一个参数z是无意义的呢,意思是f(z)是无结果的或结果非法,那么此时可以说f(z)等于自身吗?那显然是不行的。这个例子和浮点数的例子是一个意思。
然后从集合类型的角度再来看一次函数,假设有函数A=f(x)、B=g(x),注意是两个不同的函数,当二者给定一个相同输入x产生相同结果时,此时f(x)和g(x)是相等还是不等呢?与集合类似,实际应用中,这里也是由我们定义(Impl)了一种函数中的特殊相等关系,称为函数的相等。这个特殊关系(实现逻辑)中,我们只要求两个函数执行结果的值相同,不要求函数执行过程相同。
1.2 部分相等与全相等的关系
部分相等是全相等关系的子集,也就是说,如果两个元素具有相等关系,那它们之间也一定有部分相等关系。这在编程语言中的实现也是同样遵循的规则。
1.3 小结
数学中定义了(全)相等关系(等价关系)的三大性质,分别是自反性、对称性和传递性;但某些数据类型中的值或属性违背了三大性质,就不能叫做满足全相等关系, 此时只能为该类型实现部分相等关系。
在部分相等关系中,用于比较的值也是满足三大性质的,因为此时我们排除了那些特殊值。另外,部分相等是全相等关系的子集。
2. 编程与数学的关系
数学是一门研究数据、空间和变化的庞大学科,它提供了一种严谨的描述和处理问题的方式,而编程则是将问题的解决方法转化为计算机程序的过程,可以说,数学是问题的理论形式, 编程则是问题的代码形式,编程解决问题的依据来自数学。
所以说,编程语言的设计中也是大量运用了数学概念与模型的,本文关注的相等关系就是一个例子。
在Rust库中的PartialEq
的注释文档中提到了partial equivalence relations 即部分相等关系这一概念,并且同样使用了浮点数的特殊值NaN来举例说明。
Eq
的注释文档则是提到了equivalence relations,并且明确说明了,对于满足Eq
trait的类型,是一定满足相等关系的三大性质的。
3. PartialEq
3.1 trait定义
Rust中的PartialEq的命名明确地表达了它的含义,但如果我们忘记了数学中的相等关系,就肯定会对此感到疑惑。先来看看它的定义:
pub trait PartialEq<Rhs: ?Sized = Self> {
fn eq(&self, other: &Rhs) -> bool;
fn ne(&self, other: &Rhs) -> bool {
!self.eq(other)
}
}
在这个定义中,可以得到三个基本信息:
这个trait包含2个方法,eq和ne,且ne具有默认实现,使用时开发者只需要实现eq方法即可(库文档也特别说明,若没有更好的理由,则不应该手动实现ne方法);
PartialEq绑定的Rhs参数类型是
?Size
,即包括动态大小类型(DST)和固定大小类型(ST)类型(Rhs是主类型用来比较的类型);Rhs参数提供了默认类型即
Self
(和主类型一致),但也可以是其他类型,也就是说,实践中你甚至可以将i32
与struct进行比较,只要实现了对应的PartialEq
;
Rust中的lhs和rhs指的是,"left-hand side"(左手边) 和 "right-hand side"(右手边)的参数。