在 Rust 中,PartialOrd 和 Ord 是定义类型排序能力的两个核心 trait。简单说:**PartialOrd 表示"部分排序",Ord 表示"全序"**。所有需要比较大小、排序的数据结构都离不开它们。
继承关系:
rust
PartialEq → Eq
PartialEq → PartialOrd // PartialOrd 必须实现 PartialEq
PartialEq、 Eq 、PartialOrd → Ord // Ord 必须实现 PartialEq、 Eq 、PartialOrd
1、x.partial_cmp(&y) 方法,大小比较
x.partial_cmp(&y) 的返回类型是 Option<Ordering>,所以一共有 4 种可能的结果:
| 结果 | 含义 |
|---|---|
Some(Ordering::Less) |
x 小于 y |
Some(Ordering::Equal) |
x 等于 y |
Some(Ordering::Greater) |
x 大于 y |
None |
x 与 y 无法比较 (例如涉及 NaN) |
rust
fn main() {
let x = 50;
let y = 60;
println!("{:?}", x.partial_cmp(&y)); // Some(Less)
}
1、PartialOrd------部分排序
-
PartialOrd**要求类型必须先实现PartialEq**,因为排序的前提是能判断相等。 -
部分排序允许两个值之间可能无法比较 。最典型的例子就是浮点数中的
NaN:NaN与任何值(包括它自己)比较都是无意义的,因此浮点数只能实现PartialOrd。 -
PartialOrdtrait 的核心方法是partial_cmp:
rust
fn partial_cmp(&self, other: &Self) -> Option<Ordering>;
- 它返回
Option<Ordering>,当值无法排序时返回None。
rust
fn main() {
let x = 1.0f64;
let y = f64::NAN;
println!("{:?}", x.partial_cmp(&y)); // None
}
PartialOrd还提供了<、>、<=、>=这些比较运算符的默认实现,内部都依赖partial_cmp。
(1)x.partial_cmp(&y) 方法,大小比较
x.partial_cmp(&y) 的返回类型是 Option<Ordering>,所以一共有 4 种可能的结果:
| 结果 | 含义 |
|---|---|
Some(Ordering::Less) |
x 小于 y |
Some(Ordering::Equal) |
x 等于 y |
Some(Ordering::Greater) |
x 大于 y |
None |
x 与 y 无法比较 (例如涉及 NaN) |
rust
fn main() {
let x = 50;
let y = 60;
println!("{:?}", x.partial_cmp(&y)); // Some(Less)
}
(2)自动派生PartialOrd
当在结构体或枚举上 #[derive(PartialEq, PartialOrd)] 时:
-
结构体:按照字段从上到下的声明顺序,依次比较每个字段,第一个不等字段的结果即为整体结果。
rust#[derive(PartialEq, PartialOrd)] struct MyStruct { a: i32, b: i32, } fn main() { let m1 = MyStruct { a: 20, b: 55 }; let m2 = MyStruct { a: 20, b: 10 }; println!("{:?}", m1.partial_cmp(&m2)); // Some(Greater) } -
枚举:按照变体从上到下的声明顺序,靠前的变体小于靠后的变体;同一变体内的字段再按顺序比较。
rust#[derive(PartialEq, PartialOrd)] enum MyEnum { BBB, AAA, CCC, } fn main() { let m1 = MyEnum::BBB; let m2 = MyEnum::AAA; let m3 = MyEnum::CCC; println!("{:?}", m1.partial_cmp(&m2)); // Some(Less) println!("{:?}", m2.partial_cmp(&m3)); // Some(Less) }
2、Ord------全部排序
全序要求任意两个值都可以且必须能排出确定的大小,满足三个性质:
- 完全性 :对于任意
a、b,a<b、a==b、a>b三者有且只有一个成立。 - 反对称性 :若
a<b则不可能a>b。 - 传递性 :若
a<b且b<c则a<c。
Ord 的核心方法是 cmp,直接返回 Ordering(不再有 Option):
rust
fn cmp(&self, other: &Self) -> Ordering;
因为任何两个值都能比较,所以 cmp 总是能返回 Less、Equal 或 Greater。
Ord 还提供了实用的 max 和 min 方法,可以直接找出两个值中的较大者或较小者。
(1)x.cmp(&y) 方法,大小比较
x.cmp(&y) 的返回类型是 Ordering,所以一共有 4 种可能的结果**:
| 结果 | 含义 |
|---|---|
Ordering::Less |
x 小于 y |
Ordering::Equal |
x 等于 y |
Ordering::Greater |
x 大于 y |
rust
fn main() {
let x = 50;
let y = 60;
println!("{:?}", x.cmp(&y)); // Less
}
(2)自动派生Ord
当在结构体或枚举上 #[derive(PartialEq, PartialOrd, Eq, Ord)] 时:
-
结构体:按照字段从上到下的声明顺序,依次比较每个字段,第一个不等字段的结果即为整体结果。
rust#[derive(PartialEq, PartialOrd, Eq, Ord)] struct MyStruct { a: i32, b: i32, } fn main() { let m1 = MyStruct { a: 20, b: 55 }; let m2 = MyStruct { a: 20, b: 10 }; println!("{:?}", m1.cmp(&m2)); // Greater } -
枚举:按照变体从上到下的声明顺序,靠前的变体小于靠后的变体;同一变体内的字段再按顺序比较。
rust#[derive(PartialEq, PartialOrd, Eq, Ord)] enum MyEnum { BBB, AAA, CCC, } fn main() { let m1 = MyEnum::BBB; let m2 = MyEnum::AAA; let m3 = MyEnum::CCC; println!("{:?}", m1.cmp(&m2)); // Less println!("{:?}", m2.cmp(&m3)); // Less }
3、手动实现 PartialOrd 和 Ord,比较一个值
rust
// PartialOrd 必须实现 PartialEq
// Ord 必须实现 PartialEq、 Eq、PartialOrd
#[derive(PartialEq, Eq)] // 通过派生实现 PartialEq, Eq
struct MyStruct {
name: String,
age: i32,
}
// 手动实现 PartialOrd
impl PartialOrd for MyStruct {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.age.partial_cmp(other.age) // 仅比较年龄
}
}
// 手动实现 Ord
impl Ord for MyStruct {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.age.cmp(other.age) // 仅比较年龄
}
}
}
fn main() {
let m1 = MyStruct { name: "小李".to_string(), age: 55 };
let m2 = MyStruct { name: "小刘".to_string(), age: 10 };
let m3 = MyStruct { name: "小王".to_string(), age: 80 };
println!("{:?}", m1.partial_cmp(&m2)); // Some(Greater)
println!("{:?}", m1.partial_cmp(&m3)); // Some(Less)
println!("{:?}", m1.cmp(&m2)); // Greater
println!("{:?}", m1.cmp(&m3)); // Less
}
4、手动实现 Ord,比较多个值
rust
// Ord 必须实现 PartialEq、 Eq、PartialOrd
#[derive(PartialEq, Eq, PartialOrd)]
struct MyStruct {
a: i32,
b: i32,
c: i32,
}
// 手动实现 Ord
impl Ord for MyStruct {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.b.cmp(&other.b) // 先比较 b
.then(self.c.cmp(&other.c)) // 再比较 c
.then(self.a.cmp(&other.a)) // 最后比较 a
}
}
fn main() {
let m1 = MyStruct { a: 10, b: 5, c: 8 };
let m2 = MyStruct { a: 5, b: 8, c: 8 };
let m3 = MyStruct { a: 15, b: 5, c: 4 };
println!("{:?}", m1.partial_cmp(&m2)); // Some(Greater) 自动派生 abc顺序 m1.a > m2.a
println!("{:?}", m1.partial_cmp(&m3)); // Some(Less) 自动派生 abc顺序 m1.a < m3.a
println!("{:?}", m1.cmp(&m2)); // Less m1.b < m2.b
println!("{:?}", m1.cmp(&m3)); // Greater m1.c > m3.c
}