工信部网站备案修改,深圳找网站建设,徐老师在那个网站做发视频,台州wordpress前言
本文将围绕对象#xff1a;PartialEq和Eq#xff0c;以及PartialOrd和Ord#xff0c;即四个Rust中重点的Compare Trait进行讨论并解释其中的细节#xff0c;内容涵盖理论以及代码实现。
在正式介绍PartialEq和Eq、以及PartialOrd和Ord之前#xff0c;本文会首先介绍…前言
本文将围绕对象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.mdCSDNhttps://blog.csdn.net/sc_lilei/article/details/129322616发布时间2023年03月03日LicenseCC-BY-NC-SA 4.0 转载请注明作者及来源
1. 数学中的相等关系
在初中数学中会介绍到什么是相等关系也叫等价关系相等关系是一种基本的二元关系它描述了两个对象之间的相等性质。它必须满足如下三个性质
自反性反身性自己一定等于自己即aa对称性若有ab则有ba传递性若有ab和bc则有ac
也就是说满足这三个性质才叫满足完全相等关系。这很容易理解就不过多解释。
1.1 部分相等关系
对于简单的整数类型、字符串类型我们可以说它们具有完全相等关系因为它们可以全方位比较包含两个维度第一个是类型空间中的任意值第二个是每个值的任意成员属性 但是对于某些类型就不行了这些类型总是不满足其中一个维度 。下面一起来看看 以字符串为例全方位比较的是它的每个字节值以及整个字符串的长度。 0. 浮点数类型
在浮点数类型中有个特殊的值是NaNNot-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角度来看函数假设有函数Af(x)、Bf(y)若xy那显然A的值也等于B但是如果存在一个参数z是无意义的呢意思是f(z)是无结果的或结果非法那么此时可以说f(z)等于自身吗 那显然是不行的。这个例子和浮点数的例子是一个意思。
然后从集合类型的角度再来看一次函数假设有函数Af(x)、Bg(x)注意是两个不同的函数当二者给定一个相同输入x产生相同结果时此时f(x)和g(x)是相等还是不等呢 与集合类似实际应用中这里也是由我们定义Impl了一种函数中的特殊相等关系称为函数的相等。这个特殊关系实现逻辑中我们只要求两个函数执行结果的值相同不要求函数执行过程相同。
1.2 部分相等与全相等的关系
部分相等是全相等关系的子集也就是说如果两个元素具有相等关系那它们之间也一定有部分相等关系。这在编程语言中的实现也是同样遵循的规则。
1.3 小结
数学中定义了全相等关系等价关系的三大性质分别是自反性、对称性和传递性但某些数据类型中的值或属性违背了三大性质就不能叫做满足全相等关系 此时只能为该类型实现部分相等关系。
在部分相等关系中用于比较的值也是满足三大性质的因为此时我们排除了那些特殊值。另外部分相等是全相等关系的子集。
2. 编程与数学的关系
数学是一门研究数据、空间和变化的庞大学科它提供了一种严谨的描述和处理问题的方式而编程则是将问题的解决方法转化为计算机程序的过程可以说数学是问题的理论形式 编程则是问题的代码形式编程解决问题的依据来自数学。
所以说编程语言的设计中也是大量运用了数学概念与模型的本文关注的相等关系就是一个例子。
在Rust库中的PartialEq的注释文档中提到了partial equivalence relations 即部分相等关系这一概念并且同样使用了浮点数的特殊值NaN来举例说明。 Eq的注释文档则是提到了equivalence relations并且明确说明了对于满足Eqtrait的类型是一定满足相等关系的三大性质的。 3. PartialEq
3.1 trait定义
Rust中的PartialEq的命名明确地表达了它的含义但如果我们忘记了数学中的相等关系就肯定会对此感到疑惑。先来看看它的定义
pub trait PartialEqRhs: ?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”右手边的参数。 3.2 对应操作符
这个比较简单PartialEq和Eq一致拥有的eq和ne方法分别对应和!两个操作符。Rust的大部分基本类型如整型、浮点数、字符串等都实现了PartialEq 所以它们可以使用和!进行相等性比较。
3.3 可派生
英文描述为Derivable即通过derive宏可以为自定义复合类型struct/enum/union类型自动实现PartialEq用法如下
#[derive(PartialEq)]
struct Book {name: String,
}#[derive(PartialEq)]
enum BookFormat { Paperback, Hardback, Ebook }#[derive(PartialEq)]
union T {a: u32,b: f32,c: f64,
}需要注意的是可派生的前提是这个复合类型下的所有成员字段都是支持PartialEq的下面的代码说明了这种情况
// #[derive(PartialEq)] // 取消注释即可编译通过
enum BookFormat { Paperback, Hardback, Ebook }// 无法编译
#[derive(PartialEq)]
struct Book {name: String,format: BookFormat, // 未实现PartialEq
}扩展使用cargo expand命令可以打印出宏为类型实现的PartialEq代码。 3.4 手动实现PartialEq
以上一段代码为例我们假设BookFormat是引用其他crate下的代码无法为其添加derive语句不能修改它此时就需要手动为Book手动实现PartialEq代码如下
enum BookFormat { Paperback, Hardback, Ebook }struct Book {name: String,format: BookFormat,
}// 要求只要name相等则Book相等假设format无法进行相等比较
impl PartialEq for Book {fn eq(self, other: Self) - bool {self.name other.name}
}fn main() {let bk Book { name: x.to_string(), format: BookFormat::Ebook };let bk2 Book { name: x.to_string(), format: BookFormat::Paperback };assert!(bk bk2); // 因为Book实现了PartialEq所以可以比较相等性
}3.5 比较不同的类型
根据上面的trait定义中我们知道了只要在实现PartialEq时关联不同类型的Rhs参数就能比较不同类型的相等性。示例代码如下
#[derive(PartialEq)]
enum WheelBrand {Bmw,Benz,Michelin,
}struct Car {brand: WheelBrand,price: i32,
}impl PartialEqWheelBrand for Car {fn eq(self, other: WheelBrand) - bool {self.brand *other}
}fn main() {let car Car { brand: WheelBrand::Benz, price: 10000 };let wheel WheelBrand::Benz;// 比较 struct和enumassert!(car wheel);// assert!(wheel car); // 无法反过来比较
}需要注意的是代码片段中仅实现了Car与Wheel的相等性比较若要反过来比较还得提供反向的实现如下
impl PartialEqCar for WheelBrand {fn eq(self, other: Car) - bool {*self other.brand}
}3.6 Rust基本类型如何实现PartialEq
上文说过Rust的基本类型都实现了PartialEq那具体是怎么实现的呢是为每个类型都写一套impl代码吗代码在哪呢
如果你使用IDE可以通过在任意位置按住ctrl键视IDE而定点击代码中的PartialEq以打开其在标准库中的代码文件cmp.rs相对路径是RUST_LIB_DIR/core/src/cmp.rs 。 在该文件中可以找到如下宏代码
mod impls {// ...macro_rules! partial_eq_impl {($($t:ty)*) ($(#[stable(feature rust1, since 1.0.0)]#[rustc_const_unstable(feature const_cmp, issue 92391)]impl const PartialEq for $t {#[inline]fn eq(self, other: $t) - bool { (*self) (*other) }#[inline]fn ne(self, other: $t) - bool { (*self) ! (*other) }})*)}partial_eq_impl! {bool char usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64}// ...
}这里使用了Rust强大的宏特性此处使用的是声明宏还算简单来为Rust的众多基本类型快速实现了PartialEq trait。如果你还不了解宏可以暂且理解其是一种编写重复模式代码规则的编程特性它可以减少大量重复代码。
4. Eq
理解了PartialEq那Eq理解起来就非常简单了本节的内容主体与PartialEq基本一致所以相对简明。
4.1 trait定义
如下
pub trait Eq: PartialEqSelf {fn assert_receiver_is_total_eq(self) {}
}根据代码可以得到两个重要信息
Eq是继承自PartialEq的Eq相对PartialEq只多了一个方法assert_receiver_is_total_eq()并且有默认实现
第一个既然Eq继承自PartialEq说明想要实现Eq必先实现PartialEq。第二个是这个assert_receiver_is_total_eq() 方法了简单来说它是被derive语法内部使用的用来断言类型的每个属性都实现了Eq特性对于使用者的我们来说 其实不用过多关注。
4.2 对应操作符
与PartialEq无差别略。
4.3 可派生
与PartialEq的使用相似只是要注意派生时由于继承关系Eq和PartialEq必须同时存在。
#[derive(PartialEq, Eq)] // 顺序无关
struct Book {name: String,
}4.4 手动实现Eq
直接看代码
enum BookFormat { Paperback, Hardback, Ebook }struct Book {name: String,format: BookFormat,
}// 要求只要name相等则Book相等假设format无法进行相等比较
impl PartialEq for Book {fn eq(self, other: Self) - bool {self.name other.name}
}impl Eq for Book {}fn main() {let bk Book { name: x.to_string(), format: BookFormat::Ebook };let bk2 Book { name: x.to_string(), format: BookFormat::Paperback };assert!(bk bk2);
}需要注意的是必须先实现PartialEq再实现Eq。另外这里能看出的是在比较相等性方面Eq和PartialEq都是使用和!操作符无差别感知。
4.5 比较不同的类型
与PartialEq无差别略。
4.6 Rust基本类型如何实现Eq
与PartialEq一样在相对路径为RUST_LIB_DIR/core/src/cmp.rs的文件中存在如下宏代码
mod impls {/*... (先实现PartialEq)*/// 再实现Eqmacro_rules! eq_impl {($($t:ty)*) ($(#[stable(feature rust1, since 1.0.0)]impl Eq for $t {})*)}eq_impl! { () bool char usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 }
}5. 对浮点数的测试
目前在标准库中笔者只发现有浮点数是只实现了PartialEq的以及包含浮点数的复合类型下面是浮点数的测试代码
fn main() {fn check_eq_implI: Eq(typ: I) {}// check_eq_impl(0.1f32); // 编译错误// check_eq_impl(0.1f64); // 编译错误let nan f32::NAN;let infinity f32::INFINITY;let neg_infinity f32::NEG_INFINITY;assert_ne!(nan, nan); // 不等assert_eq!(infinity, infinity); // 相等assert_eq!(neg_infinity, neg_infinity); // 相等
}6. PartialOrd和Ord
6.1 与PartialEq和Eq的关系
很多时候当我们谈到PartialEq和Eq时PartialOrd和Ord总是不能脱离的话题因为它们都是一种二元比较关系前两者是相等性比较后两者是有序性也可称大小性比较。 前两者使用的操作符是和! 后两者使用的操作符是、 、没错PartialOrd和Ord的比较结果是包含等于的然后我们可以基于这个有序关系来对数据进行排序sort。 重点有序性包含相等性。 与PartialEq存在的原因一样PartialOrd的存在的理由也是因为有一些类型是不具有有序性关系的无法比较比如浮点数、Bool、Option、函数、闭包等类型。
PartialEq和Eq、PartialOrd和Ord共同描述了Rust中任意类型的二元比较关系包含相等性、有序性。 所以在上文中你可能也观察到PartialOrd和Ord的定义也位于cmp.rs文件中。 我们可以将PartialOrd和Ord直译为偏序和全序关系因为这确实是它们要表达的含义。偏序和全序的概念来自离散数学下文详解。 6.2 基本性质
PartialOrd和Ord也是满足一定的基本性质的PartialOrd满足
传递性若有ab、bc则ac。且和也是一样的对立性若有ab则ba
Ord基于PartialOrd自然遵循传递性和对立性另外对于任意两个元素还满足如下性质
确定性必定存在或或其中的一个关系
6.3 trait定义
1. PartialOrd trait
// 二元关系定义,,
pub enum Ordering {Less -1,Equal 0,Greater 1,
}pub trait PartialOrdRhs: ?Sized Self: PartialEqRhs {fn partial_cmp(self, other: Rhs) - OptionOrdering;fn lt(self, other: Rhs) - bool {matches!(self.partial_cmp(other), Some(Less))}fn le(self, other: Rhs) - bool {matches!(self.partial_cmp(other), Some(Less | Equal))}fn gt(self, other: Rhs) - bool {matches!(self.partial_cmp(other), Some(Greater))}fn ge(self, other: Rhs) - bool {matches!(self.partial_cmp(other), Some(Greater | Equal))}
}基本信息
PartialOrd继承自PartialEq这很好理解无法比较大小的类型也一定不能进行相等性比较提供partial_cmp()方法用于主类型和可以是其他类型的参数比较返回的OptionOrdering表示两者关系可以是无法比较的None那么这里我们就可以联想到Ord trait返回的肯定是Ordering因为具有全序的类型不会存在无法比较的情况另外四个方法分别实现了对应的操作符:, , , 即实现了PartialOrd的类型可以使用这些操作符进行比较除此之外由于继承了PartialEq所以还允许使用,!
请再次记住不管是PartialOrd还是Ord都包含相等关系。
2. Ord trait
pub trait Ord: Eq PartialOrdSelf {// 方法1fn cmp(self, other: Self) - Ordering;// 方法2fn max(self, other: Self) - SelfwhereSelf: Sized,Self: ~ const Destruct,{// HACK(fee1-dead): go back to using self.max_by(other, Ord::cmp)// when trait methods are allowed to be used when a const closure is// expected.match self.cmp(other) {Ordering::Less | Ordering::Equal other,Ordering::Greater self,}}// 方法3fn min(self, other: Self) - SelfwhereSelf: Sized,Self: ~ const Destruct,{// HACK(fee1-dead): go back to using self.min_by(other, Ord::cmp)// when trait methods are allowed to be used when a const closure is// expected.match self.cmp(other) {Ordering::Less | Ordering::Equal self,Ordering::Greater other,}}// 方法4fn clamp(self, min: Self, max: Self) - SelfwhereSelf: Sized,Self: ~ const Destruct,Self: ~ const PartialOrd,{assert!(min max);if self min {min} else if self max {max} else {self}}
}基本信息
cmp方法用于比较self与参数other的二元关系返回Ordering类型区别于PartialOrd.partial_cmp()返回的OptionOrderingOrd继承自EqPartialOrd这也很好理解具有全序关系的类型自然具有偏序关系提供min/max()方法以返回self与参数other之间的较小值/较大值额外提供clamp()方法返回输入的参数区间内的值显然由于继承了PartialOrd所以实现了Ord的类型可以使用操作符, , , , , ! 对Self: ~ const Destruct的解释位于where后即是类型约束这里约束了Self类型必须是实现了Destructtrait的一个指向常量的裸指针。 全序和偏序的概念来自离散数学 全序即全序关系自然也是一种二元关系。全序是指集合中的任两个元素之间都可以比较的关系。比如实数中的任两个数都可以比较大小那么“大小”就是实数集的一个全序关系。偏序集合中只有部分元素之间可以比较的关系。比如复数集中并不是所有的数都可以比较大小那么“大小”就是复数集的一个偏序关系。显然全序关系必是偏序关系。反之不成立。 6.4 可派生
1. PartialOrd derive
PartialOrd和Ord也是可以使用derive宏进行自动实现的代码如下
#[derive(PartialOrd, PartialEq)]
struct Book {name: String,
}#[derive(PartialOrd, PartialEq)]
enum BookFormat { Paperback, Hardback, Ebook }这里有几点需要注意
由于继承关系所以必须同时派生PartialEq与PartialEq相比不支持为union类型派生对struct进行派生时大小顺序依据的是成员字段的字典序字母表中的顺序数字与字母比较则根据ASCII表编码数字编码字母编码若比较多字节字符如中文则转Unicode编码后再比较; 实际上ASCII表中的字符编码与对应Unicode编码一致对enum进行派生时大小顺序依据的是枚举类型的值大小默认情况下第一个枚举类型的值是1向下递增1所以第一个枚举最小
下面使用代码对第23点举例说明
#[derive(PartialOrd, PartialEq)]
struct Book {name: String,
}
assert!(Book { name: a.to_string() } Book { name: b.to_string() });
assert!(Book { name: b.to_string() } Book { name: c.to_string() });
// 字典序中数字字母按ASCII编码排序
assert!(Book { name: 1.to_string() } Book { name: 2.to_string() });
assert!(Book { name: 2.to_string() } Book { name: a.to_string() });
// 字典序中如果比较多字节字符则先转为其Unicode的十六进制形式然后逐字节比较
// 比如 中文 曜 和 耀 的Unicode编码分别为0x66DC和0x8000所以前者小于后者
assert_eq!(曜, \u{66dc});
assert_eq!(耀, \u{8000});
assert!(Book { name: 曜.to_string() } Book { name: 耀.to_string() });#[derive(PartialOrd, PartialEq)]
enum BookFormat {Paperback,// 1Hardback,// 2Ebook, // 3
}
assert!(BookFormat::Paperback BookFormat::Hardback);
assert!(BookFormat::Hardback BookFormat::Ebook);#[derive(PartialOrd, PartialEq)]
enum BookFormat2 {// 手动指定枚举的值则可以改变它们的大小顺序Paperback 3,Hardback 2,Ebook 1,
}
assert!(BookFormat2::Paperback BookFormat2::Hardback);
assert!(BookFormat2::Hardback BookFormat2::Ebook);对于字典序比较规则还有一些特殊情况如下
如果元素A是元素B的前缀则元素A元素B空字符序列非空字序列
2. Ord derive #[derive(Ord, Eq, PartialOrd, PartialEq)]
struct Book {name: String,
}#[derive(Ord, Eq, PartialOrd, PartialEq)]
enum BookFormat {Paperback,Hardback,Ebook,
}这里只需注意一点那就是由于继承关系Ord需要和Eq, PartialOrd, PartialEq同时派生。另外根据前面所提到的PartialOrd和Ord都支持, 这个要记得
6.5 手动实现PartialOrd和Ord
1. PartialOrd Impl
// 注意这里测试对象是Book3不要为成员字段format即BookFormat3派生任何trait模拟实际项目中无法修改成员字段特性的情况
enum BookFormat3 {Paperback,Hardback,Ebook,
}struct Book3 {name: String,format: BookFormat3,
}// -- 先得实现 PartialEq
impl PartialEqSelf for Book3 {// tips这里可以将Self省略fn eq(self, other: Self) - bool {self.name other.name// 这里假设format字段不要求比较}
}// -- 才能实现 PartialOrd
impl PartialOrd for Book3 {fn partial_cmp(self, other: Self) - OptionOrdering {// 直接调用name(String)的比较方法如果成员字段也没有实现PartialOrd那就得先为成员实现这类情况很少self.name.partial_cmp(other.name)}
}2. Ord Impl
// 测试对象Book3
// - 这里同样没有使用任何derive全手动实现由于继承关系需要实现四个trait
// - 注意若存在任一成员字段(这里指 format字段)未实现PartialEq/Eq/PartialOrd都是无法为Book3派生Ord的派生时不会解析下面的手动impl
enum BookFormat3 {Paperback,Hardback,Ebook,
}struct Book3 {name: String,format: BookFormat3,
}// -- 先实现 PartialEq
impl PartialEq for Book3 {fn eq(self, other: Book3) - bool {self.name other.name// 这里假设format字段不要求比较}
}// -- 再实现 Eq
impl Eq for Book3 {}// -- 再实现 Ord
impl Ord for Book3 {fn cmp(self, other: Book3) - Ordering {// 直接调用name(String)的cmp方法当需要实现Ord时成员字段一般都实现了Ord可直接调用其cmp方法self.name.cmp(other.name)}
}// -- 最后实现 PartialOrd
impl PartialOrd for Book3 {fn partial_cmp(self, other: Book3) - OptionOrdering {// 直接调用上面实现的cmp方法Some(self.cmp(other))}
}阅读此代码需要注意几点
先读完代码注释请注意是先实现Ord再实现PartialOrd理由是既然一开始就想要为类型实现Ord说明类型是能够得出一个肯定结果的非None所以后实现PartialOrd直接调用Ord的cmp()
6.6 比较不同的类型
这一节不贴代码了留给读者去实现。具体实现手法可参考前面3.5节或4.5节中的内容。
6.7 Rust基本类型如何实现PartialOrd和Ord
1. PartialOrd impl macro
我们以前面介绍过的同样的方式找到cmp.rs中PartialOrd的实现宏代码如下
mod impls {// ... 前面是PartialEq和Eq的宏实现macro_rules! partial_ord_impl {($($t:ty)*) ($(#[stable(feature rust1, since 1.0.0)]#[rustc_const_unstable(feature const_cmp, issue 92391)]impl const PartialOrd for $t {#[inline]fn partial_cmp(self, other: $t) - OptionOrdering {// 注意看此处是根据两个比较结果来得到最终结果本质上是要求比较的值满足对立性浮点数NaN不满足所以返回Nonematch (*self *other, *self *other) {(false, false) None,(false, true) Some(Greater),(true, false) Some(Less),(true, true) Some(Equal),}}#[inline]fn lt(self, other: $t) - bool { (*self) (*other) }#[inline]fn le(self, other: $t) - bool { (*self) (*other) }#[inline]fn ge(self, other: $t) - bool { (*self) (*other) }#[inline]fn gt(self, other: $t) - bool { (*self) (*other) }})*)}#[stable(feature rust1, since 1.0.0)]#[rustc_const_unstable(feature const_cmp, issue 92391)]impl const PartialOrd for () {#[inline]fn partial_cmp(self, _: ()) - OptionOrdering {Some(Equal)}}#[stable(feature rust1, since 1.0.0)]#[rustc_const_unstable(feature const_cmp, issue 92391)]impl const PartialOrd for bool {#[inline]fn partial_cmp(self, other: bool) - OptionOrdering {Some(self.cmp(other))}}partial_ord_impl! { f32 f64 }
}这里要注意一下几点
代码中定义的宏partial_ord_impl!是通过两个比较结果来得到最终结果看注释这个宏除了应用在了浮点数类型上还应用在了()和bool类型。浮点数类型不必多说单元类型是一种单值类型用于排序的情况也比较少为bool类型实现这个trait的原因是 有时我们需要对包含bool类型成员的struct或enum进行排序所以需要为其实现PartialOrd注意其实现也是调用self.cmp() 这里的impl const中的const关键字意味着标记这个trait实现是编译时常量编译时优化以保证运行时不会有额外开销。这里是因为fn partial_cmp()的实现没有修改任何数据才可以加const当然还有其他要求例如不能使用动态分配的内存例如 Box 或 Vec、不能调用非 const 函数等 2. Ord impl macro
mod impls {// ... 前面是PartialEq/Eq/PartialOrd的宏实现macro_rules! ord_impl {($($t:ty)*) ($(#[stable(feature rust1, since 1.0.0)]#[rustc_const_unstable(feature const_cmp, issue 92391)]impl const PartialOrd for $t {#[inline]fn partial_cmp(self, other: $t) - OptionOrdering {Some(self.cmp(other))}#[inline]fn lt(self, other: $t) - bool { (*self) (*other) }#[inline]fn le(self, other: $t) - bool { (*self) (*other) }#[inline]fn ge(self, other: $t) - bool { (*self) (*other) }#[inline]fn gt(self, other: $t) - bool { (*self) (*other) }}#[stable(feature rust1, since 1.0.0)]#[rustc_const_unstable(feature const_cmp, issue 92391)]impl const Ord for $t {#[inline]fn cmp(self, other: $t) - Ordering {// The order here is important to generate more optimal assembly.// See https://github.com/rust-lang/rust/issues/63758 for more info.if *self *other { Less }else if *self *other { Equal }else { Greater }}})*)}#[stable(feature rust1, since 1.0.0)]#[rustc_const_unstable(feature const_cmp, issue 92391)]impl const Ord for () {#[inline]fn cmp(self, _other: ()) - Ordering {Equal}}#[stable(feature rust1, since 1.0.0)]#[rustc_const_unstable(feature const_cmp, issue 92391)]impl const Ord for bool {#[inline]fn cmp(self, other: bool) - Ordering {// Casting to i8s and converting the difference to an Ordering generates// more optimal assembly.// See https://github.com/rust-lang/rust/issues/66780 for more info.match (*self as i8) - (*other as i8) {-1 Less,0 Equal,1 Greater,// SAFETY: bool as i8 returns 0 or 1, so the difference cant be anything else_ unsafe { unreachable_unchecked() },}}}ord_impl! { char usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 }
}这里需要了解一下几点
实现Ord的时候需要同时实现PartialOrd不要求实现的顺序。PartialOrd的partial_cmp()内部调用的是Ord的cmp()理由前面讲过同样为()和bool类型实现了Ord为大部分基本类型char usize u8 u16 ...实现了Ord
6.8 为其他类型实现四大compare-trait
这里指的其他类型是!、不可变借用类型、可变借用类型具体实现代码就在源码中刚刚看的宏ord_impl!下方此处就不再赘述。