目录
- [1. PartialEq Trait](#1. PartialEq Trait)
-
- [1.1 跨crate注意事项](#1.1 跨crate注意事项)
- [1.2 可派生](#1.2 可派生)
- [1.3 如何实现PartialEq](#1.3 如何实现PartialEq)
- [1.4 如何比较两个不同的类型](#1.4 如何比较两个不同的类型)
- [1.5 示例](#1.5 示例)
- [1.6 必需方法](#1.6 必需方法)
- [1.7 提供方法](#1.7 提供方法)
- [2. Eq Trait(不是dyn兼容的)](#2. Eq Trait(不是dyn兼容的))
-
- [2.1 可派生](#2.1 可派生)
- [2.2 如何实现Eq](#2.2 如何实现Eq)
- [3. PartialOrd Trait](#3. PartialOrd Trait)
-
- [3.1 跨crate注意事项](#3.1 跨crate注意事项)
- [3.2 推论](#3.2 推论)
- [3.3 严格与非严格偏序](#3.3 严格与非严格偏序)
- [3.4 可派生](#3.4 可派生)
- [3.5 如何实现PartialOrd](#3.5 如何实现PartialOrd)
- [3.6 错误实现示例](#3.6 错误实现示例)
- [3.7 示例](#3.7 示例)
- [3.8 必需方法](#3.8 必需方法)
- [3.9 提供方法](#3.9 提供方法)
-
- [3.9.1 lt](#3.9.1 lt)
- [3.9.2 le](#3.9.2 le)
- [3.9.3 gt](#3.9.3 gt)
- [3.9.4 ge](#3.9.4 ge)
- [4. Ord Trait(不是dyn兼容的)](#4. Ord Trait(不是dyn兼容的))
-
- [4.1 推论](#4.1 推论)
- [4.2 可派生](#4.2 可派生)
- [4.3 字典序比较](#4.3 字典序比较)
- [4.4 如何实现Ord](#4.4 如何实现Ord)
- [4.5 错误实现示例](#4.5 错误实现示例)
- [4.6 必需方法](#4.6 必需方法)
- [4.7 提供方法](#4.7 提供方法)
-
- [4.7.1 max](#4.7.1 max)
- [4.7.2 min](#4.7.2 min)
- [4.7.3 clamp](#4.7.3 clamp)
- [5. Ordering枚举](#5. Ordering枚举)
-
- [5.1 变体](#5.1 变体)
- [5.2 方法](#5.2 方法)
-
- [5.2.1 is_eq](#5.2.1 is_eq)
- [5.2.2 is_ne](#5.2.2 is_ne)
- [5.2.3 is_lt](#5.2.3 is_lt)
- [5.2.4 is_gt](#5.2.4 is_gt)
- [5.2.5 is_le](#5.2.5 is_le)
- [5.2.6 is_ge](#5.2.6 is_ge)
- [5.2.7 reverse](#5.2.7 reverse)
- [5.2.8 then](#5.2.8 then)
- [5.2.9 then_with](#5.2.9 then_with)
- [5.3 Trait实现](#5.3 Trait实现)
-
- [5.3.1 Clone](#5.3.1 Clone)
- [5.3.2 Debug(上面的例子给出了Debug打印)](#5.3.2 Debug(上面的例子给出了Debug打印))
- [5.3.3 Hash](#5.3.3 Hash)
- [5.3.4 Ord](#5.3.4 Ord)
- [5.3.5 PartialEq](#5.3.5 PartialEq)
- [5.3.6 PartialOrd](#5.3.6 PartialOrd)
- [5.3.7 Copy](#5.3.7 Copy)
- [5.3.8 Eq](#5.3.8 Eq)
- [6. Reverse](#6. Reverse)
-
- [6.1 示例](#6.1 示例)
- [6.2 元组字段](#6.2 元组字段)
- [6.3 Trait实现](#6.3 Trait实现)
-
- [6.3.1 Clone](#6.3.1 Clone)
- [6.3.2 Debug(上面的例子给出了Debug打印)](#6.3.2 Debug(上面的例子给出了Debug打印))
- [6.3.3 Default](#6.3.3 Default)
- [6.3.4 Hash](#6.3.4 Hash)
- [6.3.5 Ord](#6.3.5 Ord)
- [6.3.6 PartialEq](#6.3.6 PartialEq)
- [6.3.7 PartialOrd](#6.3.7 PartialOrd)
- [6.3.8 Copy](#6.3.8 Copy)
- [6.3.9 Eq](#6.3.9 Eq)
- [7. 函数](#7. 函数)
-
- [7.1 max](#7.1 max)
-
- [7.1.1 示例](#7.1.1 示例)
- [7.2 max_by](#7.2 max_by)
-
- [7.2.1 示例](#7.2.1 示例)
- [7.3 max_by_key](#7.3 max_by_key)
-
- [7.3.1 示例](#7.3.1 示例)
- [7.4 min](#7.4 min)
-
- [7.4.1 示例](#7.4.1 示例)
- [7.5 min_by](#7.5 min_by)
-
- [7.5.1 示例](#7.5.1 示例)
- [7.6 min_by_key](#7.6 min_by_key)
-
- [7.6.1 示例](#7.6.1 示例)
- 参考
用于比较和排序值的工具集。
1. PartialEq Trait
rust
pub trait PartialEq<Rhs = Self>
where
Rhs: ?Sized,
{
// 必需方法
fn eq(&self, other: &Rhs) -> bool;
// 提供方法
fn ne(&self, other: &Rhs) -> bool { ... }
}
不要求Rhs在编译期是已知大小的。使用相等运算符进行比较的trait。
为类型实现此trait,即可为这些类型提供==和!=运算符。
x.eq(y)也可以写成x == y,x.ne(y)可以写成x != y。本页文档余下部分使用更易读的中缀写法。
本trait为不具备完整等价关系的类型提供相等运算符比较。例如,浮点数中NaN != NaN,因此浮点类型实现了PartialEq而未实现Eq。形式化地说,当Rhs == Self时,本trait对应一个偏等价关系。
实现必须保证eq与ne彼此一致:
a != b当且仅当!(a == b)。
ne的默认实现已提供这种一致性,且几乎总是足够。没有非常充分的理由,不应覆盖它。
如果Self和Rhs还实现了PartialOrd或Ord,它们的方法也必须与PartialEq保持一致。部分trait用派生、部分trait手动实现,很容易在无意间造成它们相互矛盾。
相等关系==必须满足以下条件(对类型A、B、C的一切a、b、c):
对称性:若A: PartialEq<B>且B: PartialEq<A>,则a == b推出b == a。
传递性:若A: PartialEq<B>、B: PartialEq<C>且A: PartialEq<C>,则a == b且b == c推出a == c。
违反这些要求属于逻辑错误。逻辑错误导致的行为是未指定的,但此trait的使用者必须确保此类逻辑错误不会导致未定义行为。这意味着unsafe代码不得依赖这些方法的正确性。
1.1 跨crate注意事项
当一个crate为另一个crate的类型实现PartialEq(例如为了让自己的某个类型能与标准库中的类型比较)时,坚守上述要求可能变得棘手。建议是:永远不要为外部类型实现此trait。换言之,这样的crate应当写impl PartialEq<ForeignType> for LocalType。
这避免了传递链跨越crate边界交错出现的问题:对所有本地类型T,你可以假设不会有其他crate添加允许比较T == U的实现。换言之,若其他crate添加了允许构建更长传递链U1 == ... == T == V1 == ...的实现,那么出现在T右侧的所有类型都必须是定义T的crate已经知晓的类型。这就排除了下游crate通过新增实现把外部类型的比较缝在一起、从而破坏传递性的可能。
不写这类针对外部类型的实现还能避免前向兼容问题:某个crate新增更多PartialEq实现,可能会导致下游crate构建失败。
1.2 可派生
此trait可与#derive一起使用。派生于结构体时,所有字段相等则两个实例相等,任一字段不等则不相等。派生于枚举时,两个实例是同一变体且所有字段相等才相等。
1.3 如何实现PartialEq
只要两本书的ISBN相同,即使装帧格式不同,也视为同一本书:
rust
enum BookFormat {
Paperback,
Hardback,
Ebook,
}
struct Book {
isbn: i32,
format: BookFormat,
}
impl PartialEq for Book {
fn eq(&self, other: &Self) -> bool {
self.isbn == other.isbn
}
}
fn main() {
let b1 = Book {
isbn: 3,
format: BookFormat::Paperback,
};
let b2 = Book {
isbn: 3,
format: BookFormat::Ebook,
};
let b3 = Book {
isbn: 10,
format: BookFormat::Paperback,
};
println!("{}", b1 == b2);
println!("{}", b1 != b3);
}

1.4 如何比较两个不同的类型
能与哪种类型比较,由PartialEq的类型参数控制。
rust
// 派生实现的是<BookFormat> == <BookFormat>的比较
#[derive(PartialEq)]
enum BookFormat {
Paperback,
Hardback,
Ebook,
}
struct Book {
isbn: i32,
format: BookFormat,
}
// 实现<Book> == <BookFormat>的比较
impl PartialEq<BookFormat> for Book {
fn eq(&self, other: &BookFormat) -> bool {
self.format == *other
}
}
// 实现<BookFormat> == <Book>的比较
impl PartialEq<Book> for BookFormat {
fn eq(&self, other: &Book) -> bool {
*self == other.format
}
}
fn main() {
let b1 = Book {
isbn: 3,
format: BookFormat::Paperback,
};
println!("{}", b1 == BookFormat::Paperback);
println!("{}", BookFormat::Ebook != b1);
}

像上面这样忽略结构体部分字段的比较可能很危险,很容易在无意间违反偏等价关系的要求。
rust
#[derive(PartialEq)]
enum BookFormat {
Paperback,
Hardback,
Ebook,
}
#[derive(PartialEq)]
struct Book {
isbn: i32,
format: BookFormat,
}
impl PartialEq<BookFormat> for Book {
fn eq(&self, other: &BookFormat) -> bool {
self.format == *other
}
}
impl PartialEq<Book> for BookFormat {
fn eq(&self, other: &Book) -> bool {
*self == other.format
}
}
fn main() {
let b1 = Book {
isbn: 1,
format: BookFormat::Paperback,
};
let b2 = Book {
isbn: 2,
format: BookFormat::Paperback,
};
println!("{}", b1 == BookFormat::Paperback);
println!("{}", BookFormat::Paperback == b2);
// 按照传递性,下面这条应当成立------但实际不成立
println!("{}", b1 == b2);
}

1.5 示例
rust
fn main() {
let x: u32 = 0;
let y: u32 = 1;
println!("{}", x == y);
println!("{}", x.eq(&y));
}

1.6 必需方法
方法签名:
rust
fn eq(&self, other: &Rhs) -> bool
检验self与other的值是否相等,供==使用。
1.7 提供方法
方法签名:
rust
fn ne(&self, other: &Rhs) -> bool
检验!=。默认实现几乎总是足够的,没有非常充分的理由不应覆盖。
2. Eq Trait(不是dyn兼容的)
rust
pub trait Eq: PartialEq {}
用于与等价关系对应的比较的trait。
与PartialEq的主要区别在于额外要求了自反性。实现了PartialEq的类型保证对所有的a、b和c:
对称性:a == b推出b == a。
传递性:a == b且b == c推出a == c。
一致性:a != b当且仅当!(a == b)。
建立在PartialEq之上的Eq还额外推出:
自反性:a == a。
这一性质无法由编译器检查,因此Eq是一个没有方法的trait。
违反这一性质属于逻辑错误。逻辑错误导致的行为是未指定的,但此trait的使用者必须确保此类逻辑错误不会导致未定义行为。这意味着unsafe代码不得依赖这些方法的正确性。
诸如f32和f64这样的浮点类型只实现了PartialEq而没有实现Eq,因为NaN != NaN。
2.1 可派生
此trait可与#derive一起使用。派生时,由于Eq没有额外方法,它只是告知编译器:这是一个等价关系,而非偏等价关系。注意,派生策略要求所有字段都是Eq,这并不总是符合需求。
2.2 如何实现Eq
如果无法使用派生策略,就声明你的类型实现Eq------它没有任何额外方法。
rust
enum BookFormat {
Paperback,
Hardback,
Ebook,
}
struct Book {
isbn: i32,
format: BookFormat,
}
impl PartialEq for Book {
fn eq(&self, other: &Self) -> bool {
self.isbn == other.isbn
}
}
impl Eq for Book {}
3. PartialOrd Trait
rust
pub trait PartialOrd<Rhs = Self>: PartialEq<Rhs>
where
Rhs: ?Sized,
{
// 必需方法
fn partial_cmp(&self, other: &Rhs) -> Option<Ordering>;
// 提供方法
fn lt(&self, other: &Rhs) -> bool { ... }
fn le(&self, other: &Rhs) -> bool { ... }
fn gt(&self, other: &Rhs) -> bool { ... }
fn ge(&self, other: &Rhs) -> bool { ... }
}
不要求Rhs在编译期是已知大小的。用于构建偏序partial order的类型的trait。
本trait的lt、le、gt、ge方法可以分别通过<、<=、>、>=运算符来调用。
只有当你计划只实现PartialOrd而不实现Ord时,才应该把一个类型的比较逻辑放在本trait中。否则,比较逻辑应该放在Ord中,本trait用Some(self.cmp(other))来实现。
本trait的方法必须彼此一致,并且与PartialEq的方法一致。以下条件必须成立:
1. a == b当且仅当partial_cmp(a, b) ==Some(Equal)。
2. a < b当且仅当partial_cmp(a, b) == Some(Less)。
3. a > b当且仅当partial_cmp(a, b) == Some(Greater)。
4. a <= b当且仅当a < b || a == b。
5. a >= b当且仅当a > b || a == b。
6. a != b当且仅当!(a == b)。
上述条件2-5由默认实现保证。条件6已由PartialEq保证。
;如果Self和Rhs还实现了Ord,那么Ord也必须与partial_cmp保持一致。通过派生一部分trait,手动实现另一部分的方式,很容易不小心让它们变得不一致。
比较关系必须满足以下条件(对所有类型A、B、C的值a、b、c):
传递性:若A: PartialOrd<B>、B: PartialOrd<C>且A: PartialOrd<C>,则a < b且b < c推出a < c。对==和>也必须同样成立。
对偶性:若A: PartialOrd<B>且B: PartialOrd<A>,则a < b当且仅当b > a。
违反这些要求属于逻辑错误。逻辑错误导致的行为是未指定的,但此trait的使用者必须确保此类逻辑错误不会导致未定义行为。这意味着unsafe代码不得依赖这些方法的正确性。
3.1 跨crate注意事项
当一个crate为另一个crate的类型实现PartialOrd时(例如为了让自己的某个类型能与标准库中的类型比较),要维持上述要求就可能变得棘手。建议是:永远不要为外部类型实现本trait。换言之,这样的crate应该写impl PartialOrd<ForeignType> for LocalType。
这样就避免了传递链跨越crate边界所带来的问题:对于所有本地类型T,你可以假定不会有其他crate添加允许比较T < U的实现。换句话说,如果其他crate添加了能构建更长传递链U1 < ... < T < V1 < ...的实现,那么出现在T右侧的所有类型都必须是定义T的那个crate已经知道的类型。这就排除了下游crate添加新的实现、以违反传递性的方式把外部类型之间的比较拼接起来的传递链。
不写这类面向外部类型的实现,还可以避免兼容性问题:某个crate日后添加更多PartialOrd实现,可能会在无意间导致下游crate构建失败。
3.2 推论
以下推论由上述要求直接得出:
<与>的反自反性:!(a < a)、!(a > a)。
>的传递性:若a > b且b > c,则a > c。
partial_cmp的对偶性:partial_cmp(a, b) == partial_cmp(b, a).map(Ordering::reverse)。
3.3 严格与非严格偏序
<与>运算符的行为符合严格偏序。然而,<=与>=并不符合非严格偏序。这是因为从数学上讲,非严格偏序要求自反性,即对每个a都必须有a <= a为真。对实现了PartialOrd的类型来说,这一点并不总是成立,例如:
rust
fn main() {
let a = f64::NAN;
println!("{}", a <= a);
}

3.4 可派生
此trait可与#derive一起使用。
在结构体上派生时,会按结构体成员从上到下的声明顺序生成字典序。
在枚举上派生时,变体首先按其判别值顺序,其次再按其字段排序。默认情况下,排在最上面的变体判别值最小,最下面的判别值最大。
rust
#[derive(PartialEq, PartialOrd)]
enum E {
Top,
Bottom,
}
fn main() {
println!("{}", E::Top < E::Bottom);
}

但是,手动设置判别值可以覆盖这一默认行为:
rust
#[derive(PartialEq, PartialOrd)]
enum E {
Top = 2,
Bottom = 1,
}
fn main() {
println!("{}", E::Bottom < E::Top);
}

3.5 如何实现PartialOrd
PartialOrd只要求实现partial_cmp方法,其余方法都会由默认实现生成。
不过,对于不存在全序total order的类型,仍然可以单独实现其他方法。例如,对浮点数而言,NaN < 0 == false且NaN >= 0 == false。
PartialOrd要求你的类型实现PartialEq。
如果你的类型是Ord,可以通过cmp来实现partial_cmp。
rust
use std::cmp::Ordering;
struct Person {
id: u32,
name: String,
height: u32,
}
impl PartialOrd for Person {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Person {
fn cmp(&self, other: &Self) -> Ordering {
self.height.cmp(&other.height)
}
}
impl PartialEq for Person {
fn eq(&self, other: &Self) -> bool {
self.height == other.height
}
}
impl Eq for Person {}
在你的类型字段上使用partial_cmp也可能很有用。下面是一个Person类的例子,它有一个浮点类型的height字段,且该字段是唯一用于排序的字段。
rust
use std::cmp::Ordering;
struct Person {
id: u32,
name: String,
height: f64,
}
impl PartialOrd for Person {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.height.partial_cmp(&other.height)
}
}
impl PartialEq for Person {
fn eq(&self, other: &Self) -> bool {
self.height == other.height
}
}
3.6 错误实现示例
rust
use std::cmp::Ordering;
#[derive(Debug, PartialEq)]
struct Character {
health: u32,
experience: u32,
}
impl PartialOrd for Character {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.health.cmp(&other.health))
}
}
fn main() {
let a = Character {
health: 10,
experience: 5,
};
let b = Character {
health: 10,
experience: 77,
};
// 错误:PartialEq与PartialOrd彼此不一致
println!("{}", a.partial_cmp(&b).unwrap() == Ordering::Equal);
println!("{}", a == b);
}

3.7 示例
rust
fn main() {
let x: u32 = 0;
let y: u32 = 1;
println!("{}", x < y);
println!("{}", x.lt(&y));
}

3.8 必需方法
方法签名:
rust
fn partial_cmp(&self, other: &Rhs) -> Option<Ordering>
此方法在self与other之间存在顺序关系时,返回该顺序关系。
rust
use std::cmp::Ordering;
fn main() {
let result = 1.0.partial_cmp(&2.0);
println!("{}", result == Some(Ordering::Less));
let result = 1.0.partial_cmp(&1.0);
println!("{}", result == Some(Ordering::Equal));
let result = 2.0.partial_cmp(&1.0);
println!("{}", result == Some(Ordering::Greater));
}

无法比较时:
rust
fn main() {
let result = f64::NAN.partial_cmp(&1.0);
println!("{}", result == None);
}

3.9 提供方法
3.9.1 lt
方法签名:
rust
fn lt(&self, other: &Rhs) -> bool
测试小于(针对self与other),供<运算符使用。
rust
fn main() {
println!("{}", 1.0 < 1.0);
println!("{}", 1.0 < 2.0);
println!("{}", 2.0 < 1.0);
}

3.9.2 le
方法签名:
rust
fn le(&self, other: &Rhs) -> bool
测试小于或等于(针对self与other),供<=运算符使用。
rust
fn main() {
println!("{}", 1.0 <= 1.0);
println!("{}", 1.0 <= 2.0);
println!("{}", 2.0 <= 1.0);
}

3.9.3 gt
方法签名:
rust
fn gt(&self, other: &Rhs) -> bool
测试大于(针对self与other),供>运算符使用。
rust
fn main() {
println!("{}", 1.0 > 1.0);
println!("{}", 1.0 > 2.0);
println!("{}", 2.0 > 1.0);
}

3.9.4 ge
方法签名:
rust
fn ge(&self, other: &Rhs) -> bool
测试大于或等于(针对self与other),供>=运算符使用。
rust
fn main() {
println!("{}", 1.0 >= 1.0);
println!("{}", 1.0 >= 2.0);
println!("{}", 2.0 >= 1.0);
}

4. Ord Trait(不是dyn兼容的)
rust
pub trait Ord: Eq + PartialOrd {
// 必需方法
fn cmp(&self, other: &Self) -> Ordering;
// 提供方法
fn max(self, other: Self) -> Self
where Self: Sized,
{ ... }
fn min(self, other: Self) -> Self
where Self: Sized,
{ ... }
fn clamp(self, min: Self, max: Self) -> Self
where Self: Sized,
{ ... }
}
用于构建全序total order的类型的trait。
实现必须与PartialOrd的实现保持一致,并确保max、min、clamp与cmp一致:
partial_cmp(a, b) == Some(cmp(a, b))。
max(a, b) == max_by(a, b, cmp)(由默认实现确保)。
min(a, b) == min_by(a, b, cmp)(由默认实现确保)。
对a.clamp(min, max),参见该方法(由默认实现确保)。
违反这些要求属于逻辑错误。逻辑错误导致的未定义行为是未指定的,但此trait的使用者必须确保此类逻辑错误不会导致未定义行为。这意味着unsafe代码不得依赖这些方法的正确性。
4.1 推论
由上述要求以及PartialOrd的要求可以得出,对所有a、b、c:
a < b、a == b、a > b三者恰有一个为真。
且<是传递的:a < b且b < c推出a < c。对==和>也必须同样成立。
从数学上讲,<运算符定义了一个严格弱序。在==符合数学意义上相等的场合,它还定义了严格全序。
4.2 可派生
此trait可与#derive一起使用。
在结构体上派生时,会按结构体成员从上到下的声明顺序生成字典序。
在枚举上派生时,变体首先按其判别值排序,其次再按其字段排序。默认情况下,排在最上面的变体判别值最小,最下面的判别值最大。
rust
#[derive(Eq, Ord, PartialEq, PartialOrd)]
enum E {
Top,
Bottom,
}
fn main() {
println!("{}", E::Top < E::Bottom);
}

但是,手动设置判别值可以覆盖这一默认行为:
rust
#[derive(Eq, PartialEq, PartialOrd, Ord)]
enum E {
Top = 2,
Bottom = 1,
}
fn main() {
println!("{}", E::Bottom < E::Top);
}

4.3 字典序比较
字典序比较lexicographical comparison是具有以下性质的操作。
两个序列逐元素比较。
第一个不相等的元素决定哪个序列在字典序上更小或更大。
如果一个序列是另一个序列的前缀,则较短的序列在字典序上更小。
如果两个序列的元素------相等且长度相同,则两个字典序相等。
空序列在字典序上小于任何非空序列。
两个空序列字典序相等。
4.4 如何实现Ord
Ord要求类型同时实现PartialOrd、PartialEq和Eq。
由于Ord能推出比PartialOrd更强的排序关系,且Ord与PartialOrd必须保持一致,你必须先决定如何实现Ord。你可以选择派生它,也可以手动实现它。如果派生,就应当把四个trait全部派生;如果手动实现,就应当以Ord的实现为基础,手动实现全部四个trait。
rust
use std::cmp::Ordering;
struct Character {
health: u32,
experience: u32,
mana: f32,
}
impl Ord for Character {
fn cmp(&self, other: &Self) -> Ordering {
self.experience
.cmp(&other.experience)
.then(self.health.cmp(&other.health))
}
}
impl PartialOrd for Character {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for Character {
fn eq(&self, other: &Self) -> bool {
self.health == other.health && self.experience == other.experience
}
}
impl Eq for Character {}
如果你只是想按照某个字段的值对某个类型做slice::sort(切片排序),使用slice::sort_by_key可能更简单。
4.5 错误实现示例
rust
use std::cmp::Ordering;
#[derive(Debug)]
struct Character {
health: f32,
}
impl Ord for Character {
fn cmp(&self, other: &Self) -> Ordering {
if self.health < other.health {
Ordering::Less
} else if self.health > other.health {
Ordering::Greater
} else {
Ordering::Equal
}
}
}
impl PartialOrd for Character {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for Character {
fn eq(&self, other: &Self) -> bool {
self.health == other.health
}
}
impl Eq for Character {}
fn main() {
let a = Character { health: 4.5 };
let b = Character { health: f32::NAN };
// 错误:浮点值不构成全序,无视这一现实,直接用内建比较运算符去实现Ord
// 并不能改变它。如果需要浮点值的全序,请改用f32::total_cmp
// Ord的自反性要求不成立
println!("{}", a == a);
println!("{}", b != b);
// Ord的反对称性要求不成立,a < c与c < a只允许一个为真
// 不能两个都真,也不能两个都假
println!("{}", (a < b) as u8 + (b < a) as u8);
}

rust
use std::cmp::Ordering;
#[derive(Debug)]
struct Character {
health: u32,
experience: u32,
}
impl PartialOrd for Character {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Character {
fn cmp(&self, other: &Self) -> Ordering {
if self.health < 50 {
self.health.cmp(&other.health)
} else {
self.experience.cmp(&other.experience)
}
}
}
// 出于性能考虑,这样实现PartialEq并不地道,但在这个示例中
// 它保证了PartialEq、PartialOrd与Ord行为一致
impl PartialEq for Character {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == Ordering::Equal
}
}
impl Eq for Character {}
fn main() {
let a = Character {
health: 3,
experience: 5,
};
let b = Character {
health: 10,
experience: 77,
};
let c = Character {
health: 143,
experience: 2,
};
// 错误:Ord的实现会依据self.health的值去比较不同的字段,
// 得到的顺序不是全序
// Ord的传递要求不成立。若a小于b且b小于c,由传递性
// a也必须小于c
println!("{}", a < b && b < c && c < a);
// Ord的反对称性要求也不成立。a < c与c < a只允许一个为真,
// 不能两个都真,也不能两个都假
println!("{}", (a < c) as u8 + (c < a) as u8);
}

4.6 必需方法
方法签名:
rust
fn cmp(&self, other: &Self) -> Ordering
返回self与other之间的Ordering。如果表达式self <operator> other为真,则self.cmp(&other)返回与之匹配的顺序关系。
rust
fn main() {
println!("{:?}", 5.cmp(&10));
println!("{:?}", 10.cmp(&5));
println!("{:?}", 5.cmp(&5));
}

4.7 提供方法
4.7.1 max
方法签名:
rust
fn max(self, other: Self) -> Self
where
Self: Sized,
要求实现者在编译期是已知大小的。方法消耗自身和输入参数。比较并返回两个值中较大的一个。若比较判定两者相等,则返回第二个参数。
rust
fn main() {
println!("{}", 1.max(2));
println!("{}", 2.max(2));
}

rust
use std::cmp::Ordering;
#[derive(Eq)]
struct Equal(&'static str);
impl PartialEq for Equal {
fn eq(&self, _other: &Self) -> bool {
true
}
}
impl PartialOrd for Equal {
fn partial_cmp(&self, _other: &Self) -> Option<Ordering> {
Some(Ordering::Equal)
}
}
impl Ord for Equal {
fn cmp(&self, _other: &Self) -> Ordering {
Ordering::Equal
}
}
fn main() {
println!("{}", Equal("self").max(Equal("other")).0);
}

4.7.2 min
方法签名:
rust
fn min(self, other: Self) -> Self
where
Self: Sized,
要求实现者在编译期是已知大小的。方法消耗自身和输入参数。比较并返回两个值中较小的一个。若比较判定两者相等,则返回第一个参数。
rust
fn main() {
println!("{}", 1.min(2));
println!("{}", 2.min(2));
}

rust
use std::cmp::Ordering;
#[derive(Eq)]
struct Equal(&'static str);
impl PartialEq for Equal {
fn eq(&self, _other: &Self) -> bool {
true
}
}
impl PartialOrd for Equal {
fn partial_cmp(&self, _other: &Self) -> Option<Ordering> {
Some(Ordering::Equal)
}
}
impl Ord for Equal {
fn cmp(&self, _other: &Self) -> Ordering {
Ordering::Equal
}
}
fn main() {
println!("{}", Equal("self").min(Equal("other")).0);
}

4.7.3 clamp
方法签名:
rust
fn clamp(self, min: Self, max: Self) -> Self
where
Self: Sized,
要求实现者在编译期是已知大小的。方法消耗自身和输入参数。将一个值限制在某个区间内。若self大于max则返回max,若self小于min则返回min,否则返回self。min > max会panic。
rust
fn main() {
println!("{}", (-3).clamp(-2, 1));
println!("{}", 0.clamp(-2, 1));
println!("{}", 2.clamp(-2, 1));
}

5. Ordering枚举
rust
#[repr(i8)]
pub enum Ordering {
Less = -1,
Equal = 0,
Greater = 1,
}
Ordering顺序关系是两个值之间比较的结果。
5.1 变体
Less = -1
表示被比较的值小于另一个值的顺序关系。
Equal = 0
表示被比较的值等于另一个值的顺序关系。
Greater = 1
表示被比较的值大于另一个值的顺序关系。
5.2 方法
5.2.1 is_eq
方法签名:
rust
pub const fn is_eq(self) -> bool
方法消耗自身。当顺序关系是Equal变体时返回true。
rust
use std::cmp::Ordering;
fn main() {
println!("{}", Ordering::Less.is_eq());
println!("{}", Ordering::Equal.is_eq());
println!("{}", Ordering::Greater.is_eq());
}

5.2.2 is_ne
方法签名:
rust
pub const fn is_ne(self) -> bool
方法消耗自身。当顺序关系不是Equal变体时返回true。
rust
use std::cmp::Ordering;
fn main() {
println!("{}", Ordering::Less.is_ne());
println!("{}", Ordering::Equal.is_ne());
println!("{}", Ordering::Greater.is_ne());
}

5.2.3 is_lt
方法签名:
rust
pub const fn is_lt(self) -> bool
方法消耗自身。当顺序关系是Less变体时返回true。
rust
use std::cmp::Ordering;
fn main() {
println!("{}", Ordering::Less.is_lt());
println!("{}", Ordering::Equal.is_lt());
println!("{}", Ordering::Greater.is_lt());
}

5.2.4 is_gt
方法签名:
rust
pub const fn is_gt(self) -> bool
方法消耗自身。当顺序关系是Greater变体时返回true。
rust
use std::cmp::Ordering;
fn main() {
println!("{}", Ordering::Less.is_gt());
println!("{}", Ordering::Equal.is_gt());
println!("{}", Ordering::Greater.is_gt());
}

5.2.5 is_le
方法签名:
rust
pub const fn is_le(self) -> bool
方法消耗自身。当顺序关系是Less或Equal变体时返回true。
rust
use std::cmp::Ordering;
fn main() {
println!("{}", Ordering::Less.is_le());
println!("{}", Ordering::Equal.is_le());
println!("{}", Ordering::Greater.is_le());
}

5.2.6 is_ge
方法签名:
rust
pub const fn is_ge(self) -> bool
方法消耗自身。当顺序关系是Greater或Equal变体时返回true。
rust
use std::cmp::Ordering;
fn main() {
println!("{}", Ordering::Less.is_ge());
println!("{}", Ordering::Equal.is_ge());
println!("{}", Ordering::Greater.is_ge());
}

5.2.7 reverse
方法签名:
rust
pub const fn reverse(self) -> Ordering
方法消耗自身反转顺序关系。Less变为Greater,Greater变为Less,Equal保持Equal。
rust
use std::cmp::Ordering;
fn main() {
println!("{:?}", Ordering::Less.reverse());
println!("{:?}", Ordering::Equal.reverse());
println!("{:?}", Ordering::Greater.reverse());
}

此方法可用于反转比较:
rust
fn main() {
let data: &mut [_] = &mut [2, 10, 5, 8];
// 将数组从大到小排序
data.sort_by(|a, b| a.cmp(b).reverse());
let b: &mut [_] = &mut [10, 8, 5, 2];
println!("{}", data == b);
}

5.2.8 then
方法签名:
rust
pub const fn then(self, other: Ordering) -> Ordering
方法消耗自身。链接两个顺序关系。当self不是Equal时返回self,否则返回other。
rust
use std::cmp::Ordering;
fn main() {
let result = Ordering::Equal.then(Ordering::Less);
println!("{:?}", result);
let result = Ordering::Less.then(Ordering::Greater);
println!("{:?}", result);
let result = Ordering::Equal.then(Ordering::Equal);
println!("{:?}", result);
let x: (u32, u32, u32) = (1, 2, 7);
let y: (u32, u32, u32) = (1, 5, 3);
let result = x.0.cmp(&y.0).then(x.1.cmp(&y.1)).then(x.2.cmp(&y.2));
println!("{:?}", result);
}

5.2.9 then_with
方法签名:
rust
pub fn then_with<F>(self, f: F) -> Ordering
where
F: FnOnce() -> Ordering,
参数f接受任何实现了FnOnce() -> Ordering的类型,包括闭包、函数指针和函数项。方法会消耗自身。用给定的f链接顺序关系。当self不是Equal时返回self,否则调用f并返回结果。
rust
use std::cmp::Ordering;
fn main() {
let result = Ordering::Equal.then_with(|| Ordering::Less);
println!("{:?}", result);
let result = Ordering::Less.then_with(|| Ordering::Equal);
println!("{:?}", result);
let result = Ordering::Greater.then_with(|| Ordering::Less);
println!("{:?}", result);
let result = Ordering::Equal.then_with(|| Ordering::Equal);
println!("{:?}", result);
let x: (u32, u32, u32) = (1, 2, 7);
let y: (u32, u32, u32) = (1, 5, 3);
let result = x.0.cmp(&y.0).then_with(|| x.1.cmp(&y.1)).then_with(|| x.2.cmp(&y.2));
println!("{:?}", result);
}

5.3 Trait实现
5.3.1 Clone
rust
use std::cmp::Ordering;
fn main() {
let order = Ordering::Equal;
let cloned = order.clone();
println!("{:?}", cloned);
}

5.3.2 Debug(上面的例子给出了Debug打印)
5.3.3 Hash
rust
use std::cmp::Ordering;
use std::hash::{BuildHasher, Hash, Hasher, RandomState};
fn main() {
let order = Ordering::Equal;
let state = RandomState::new();
let mut hasher = state.build_hasher();
order.hash(&mut hasher);
println!("{:x}", hasher.finish());
}

5.3.4 Ord
rust
use std::cmp::Ordering;
fn main() {
println!("{}", Ordering::Less > Ordering::Greater);
println!("{}", Ordering::Less < Ordering::Equal);
println!("{}", Ordering::Greater < Ordering::Less);
}

5.3.5 PartialEq
rust
use std::cmp::Ordering;
fn main() {
println!("{}", Ordering::Less == Ordering::Equal);
println!("{}", Ordering::Equal != Ordering::Greater);
}

5.3.6 PartialOrd
5.3.7 Copy
5.3.8 Eq
6. Reverse
rust
#[repr(transparent)]
pub struct Reverse<T>(pub T);
一个用于反转排序的辅助结构体。
该结构体是与Vec::sort_by_key之类的函数配合使用的辅助工具,可用于反转键中某一部分的顺序。
6.1 示例
rust
use std::cmp::Reverse;
fn main() {
let mut v = vec![1, 2, 3, 4, 5, 6];
v.sort_by_key(|&num| (num > 3, Reverse(num)));
println!("{:?}", v);
}

6.2 元组字段
0: T
6.3 Trait实现
6.3.1 Clone
要求T也实现Clone。
rust
use std::cmp::Reverse;
fn main() {
let r = Reverse(1);
let cloned = r.clone();
println!("{:?}", cloned);
}

6.3.2 Debug(上面的例子给出了Debug打印)
要求T也实现Debug。
6.3.3 Default
要求T也实现Default。
rust
use std::cmp::Reverse;
fn main() {
let r = Reverse::<u32>::default();
println!("{:?}", r);
}

6.3.4 Hash
要求T也实现Hash。
rust
use std::cmp::Reverse;
use std::hash::{BuildHasher, Hash, Hasher, RandomState};
fn main() {
let state = RandomState::new();
let mut hasher = state.build_hasher();
Reverse(1).hash(&mut hasher);
println!("{:x}", hasher.finish());
}

6.3.5 Ord
要求T也实现Ord。
rust
use std::cmp::Reverse;
fn main() {
let r1 = Reverse(1);
let r2 = Reverse(2);
println!("{}", r1 > r2);
let r1 = Reverse("abc");
let r2 = Reverse("def");
println!("{}", r1 < r2);
}

6.3.6 PartialEq
要求T也实现PartialEq。
rust
use std::cmp::Reverse;
fn main() {
let r1 = Reverse(1);
let r2 = Reverse(2);
println!("{}", r1 == r2);
println!("{}", r1 != r1);
}

6.3.7 PartialOrd
要求T也实现PartialOrd。
6.3.8 Copy
要求T也实现Copy。
6.3.9 Eq
要求T也实现Eq。
7. 函数
7.1 max
函数签名:
rust
pub fn max<T>(v1: T, v2: T) -> T
where
T: Ord,
T必须实现Ord。比较并返回两个值中的最大值。如果比较判定两个值相等,则返回第二个值。内部使用了Ord::max的一个别名。
7.1.1 示例
rust
use std::cmp;
fn main() {
println!("{}", cmp::max(1, 2));
println!("{}", cmp::max(2, 2));
}

rust
use std::cmp::{self, Ordering};
#[derive(Eq)]
struct Equal(&'static str);
impl PartialEq for Equal {
fn eq(&self, _other: &Self) -> bool { true }
}
impl PartialOrd for Equal {
fn partial_cmp(&self, _other: &Self) -> Option<Ordering> { Some(Ordering::Equal) }
}
impl Ord for Equal {
fn cmp(&self, _other: &Self) -> Ordering { Ordering::Equal }
}
fn main() {
println!("{}", cmp::max(Equal("v1"), Equal("v2")).0);
}

7.2 max_by
方法签名:
rust
pub fn max_by<T, F>(v1: T, v2: T, compare: F) -> T
where
F: FnOnce(&T, &T) -> Ordering,
参数compare接受任何实现了FnOnce(&T, &T) -> Ordering的类型,包括闭包 、函数指针和函数项。返回两个值中相对于指定比较函数的最大值。如果比较判定两个值相等,则返回第二个参数。调用比较函数时保持参数顺序,即v1总是作为第一个参数传入,v2作为第二个参数传入。
7.2.1 示例
rust
use std::cmp;
fn main() {
let abs_cmp = |x: &i32, y: &i32| x.abs().cmp(&y.abs());
let result = cmp::max_by(3, -2, abs_cmp);
println!("{}", result);
let result = cmp::max_by(1, -2, abs_cmp);
println!("{}", result);
let result = cmp::max_by(1, -1, abs_cmp);
println!("{}", result);
}

7.3 max_by_key
方法签名:
rust
pub fn max_by_key<T, F, K>(v1: T, v2: T, f: F) -> T
where
F: FnMut(&T) -> K,
K: Ord,
参数f接受任何实现了FnMut(&T) -> K的类型,包括闭包 、函数指针和函数项。K必须实现Ord。返回按指定函数求值后取值最大的那个元素。如果比较判定两个值相等,则返回第二个参数。
7.3.1 示例
rust
use std::cmp;
fn main() {
let result = cmp::max_by_key(3, -2, |x: &i32| x.abs());
println!("{}", result);
let result = cmp::max_by_key(1, -2, |x: &i32| x.abs());
println!("{}", result);
let result = cmp::max_by_key(1, -1, |x: &i32| x.abs());
println!("{}", result);
}

7.4 min
方法签名:
rust
pub fn min<T>(v1: T, v2: T) -> T
where
T: Ord,
T必须实现Ord。比较并返回两个值中的最小值。如果比较判定两个值相等,则返回第一个值。内部使用了Ord::min的一个别名。
7.4.1 示例
rust
use std::cmp;
fn main() {
println!("{}", cmp::min(1, 2));
println!("{}", cmp::min(2, 2));
}

rust
use std::cmp::{self, Ordering};
#[derive(Eq)]
struct Equal(&'static str);
impl PartialEq for Equal {
fn eq(&self, other: &Self) -> bool { true }
}
impl PartialOrd for Equal {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(Ordering::Equal) }
}
impl Ord for Equal {
fn cmp(&self, other: &Self) -> Ordering { Ordering::Equal }
}
fn main() {
println!("{}", cmp::min(Equal("v1"), Equal("v2")).0);
}
7.5 min_by
方法签名:
rust
pub fn min_by<T, F>(v1: T, v2: T, compare: F) -> T
where
F: FnOnce(&T, &T) -> Ordering,
参数compare接受任何实现了FnOnce(&T, &T) -> Ordering的类型,包括闭包 、函数指针和函数项。返回两个值中相对于指定比较函数的最小值。如果比较判定两个值相等,则返回第一个参数。调用比较函数时保持参数顺序,即v1总是作为第一个参数插入,v2作为第二个参数传入。
7.5.1 示例
rust
use std::cmp;
fn main() {
let abs_cmp = |x: &i32, y: &i32| x.abs().cmp(&y.abs());
let result = cmp::min_by(2, -1, abs_cmp);
println!("{}", result);
let result = cmp::min_by(2, -3, abs_cmp);
println!("{}", result);
let result = cmp::min_by(1, -1, abs_cmp);
println!("{}", result);
}

7.6 min_by_key
方法签名:
rust
pub fn min_by_key<T, F, K>(v1: T, v2: T, f: F) -> T
where
F: FnMut(&T) -> K,
K: Ord,
参数f接受任何实现了FnMut(&T) -> K的类型,包括闭包 、函数指针和函数项。K必须实现Ord。返回安指定函数求值后取值最小的那个元素。如果比较判定两个值相等,则返回第一个参数。
7.6.1 示例
rust
use std::cmp;
fn main() {
let result = cmp::min_by_key(2, -1, |x: &i32| x.abs());
println!("{}", result);
let result = cmp::min_by_key(2, -3, |x: &i32| x.abs());
println!("{}", result);
let result = cmp::min_by_key(1, -1, |x: &i32| x.abs());
println!("{}", result);
}

参考
1、Rust std::cmp