概述
trait
继承关系:
都可以通过#[derive()]
的方式引入,使用其默认实现
对于浮点型来说,其值有可能是NaN
- 使用
PartialEq
(返回**false**
)和PartialOrd
(返回**None**
)时包含这种可能性 - 但是
Ord/Eq
无法对其进行比较,在Ord/Eq
的实现中:Ord
的实现是cmp
方法
error[E0599]: the method cmp
exists for type f64
, but its trait bounds were
not satisfied
—> src\main.rs:22:20
|
22 | self.price.cmp(&other.price)
| ^^^ method cannot be called on f64
due to unsatisfied trait bounds
- 当
#[derive(Eq)]
时,结构体中不能有浮点型字段:
error[E0277]: the trait bound f64: Eq
is not satisfied
—> src\main.rs:7:5
|
7 | price:f64
| ^^^^^^^^^ the trait Eq
is not implemented for f64
Ord trait
用处
只有实现了Ord trait
,才能使用sort
方法、BTree
等
Eq trait
用途
只有实现了Eq trait
,才能将此对象用作Hash
的key
PartialEq trait
定义
pub trait PartialEq<Rhs = Self> where Rhs: ?Sized, {
fn eq(&self, other: &Rhs) -> bool;
// 实现eq方法后自动实现
fn ne(&self, other: &Rhs) -> bool { ... }
}
特点
符合
- 对称性:a == b implies b == a
- 传递性:a == b and b == c implies a == c
使用
用于定义结构体/枚举的比较方式
只用实现eq
方法,ne
方法自动实现
可以使用#[derive(PartialEq)]
的方式引入,这样会使用默认实现:比较结构体的每个字段
When derived on structs, two instances are equal if all fields are equal, and not equal if any fields are not equal. When derived on enums, each variant is equal to itself and not equal to the other variants.
实现PartialEq trait
后,Rust
会默认提供一揽子泛型(generic blanket impls
)实现,自动提供引用间的比较实现:
// 不可变引用间比较
impl<A, B> PartialEq<&'_ B> for &'_ A
where A: PartialEq<B> + ?Sized, B: ?Sized;
// 可变引用与不可变引用进行比较
impl<A, B> PartialEq<&'_ B> for &'_ mut A
where A: PartialEq<B> + ?Sized, B: ?Sized;
// 不可变引用与可变引用进行比较
impl<A, B> PartialEq<&'_ mut B> for &'_ A
where A: PartialEq<B> + ?Sized, B: ?Sized;
// 可变引用之间进行比较
impl<A, B> PartialEq<&'_ mut B> for &'_ mut A
where A: PartialEq<B> + ?Sized, B: ?Sized;
实例:为结构体实现比较方法:
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
}
}
let b1 = Book { isbn: 3, format: BookFormat::Paperback };
let b2 = Book { isbn: 3, format: BookFormat::Ebook };
let b3 = Book { isbn: 10, format: BookFormat::Paperback };
assert!(b1 == b2);
assert!(b1 != b3);
实例:比较不同类型
需要通过泛型指定要比较的目标Book
与BookFormat
之间的通过比较**format**
字段的,所以需要为BookFormat
加上#[derive(PartialEq)]
// 为枚举引入PartialEq,实现枚举间的比较
#[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
}
}
let b1 = Book { isbn: 3, format: BookFormat::Paperback };
let b2 = Book { isbn: 2, format: BookFormat::Paperback };
assert!(b1 == BookFormat::Paperback);
assert!(BookFormat::Ebook != b1);
// 实现Book间的比较
// 通过自定义相同类型的比较,实现了传递性
impl PartialEq for Book {
fn eq(&self, other: &Self) -> bool {
// 指定比较的具体字段
self.format == other.format
}
}
assert!(b1 == b2);
Eq
定义
pub trait Eq: PartialEq<Self> { }
Eq
是标记trait
,它标识相等性,且要求所有字段相等,它没有要实现的方法
特点
除了对称性和传递性,还需要满足自反性:a == a
使用:
如果一个类型的全部成员都实现了 Eq
特性,那么该类型本身也可以衍生出该特性
所有的浮点类型都实现了PartialEq
但是没有实现 Eq
,因为 NaN != NaN
。
几乎所有其它实现 PartialEq
的类型也都自然地实现了 Eq
,除非它们包含了浮点数。
需要实现**PartialEq trait**
需要使用Eq trait
时,需要先实现这个类型的PartialEq trait
,然后使用#[derive(Eq)]
的方式,实现Eq trait
PartialOrd
定义
enum Ordering {
Less,
Equal,
Greater,
}
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 { ... }
}
实现PartialOrd<Rhs>
的类型可以和Rhs
的类型之间使用<,<=,>,和 >=
算符。
特点
对比较的内容,需要满足:
- 不对称性: if a < b then !(a > b), as well as a > b implying !(a < b)
- 传递性:a < b and b < c implies a < c
对于NaN
,为其比较时,会返回None
:
let result = f64::NAN.partial_cmp(&1.0);
assert_eq!(result, None);
使用
可以使用#[derive(PartialOrd)]
引入
This trait can be used with #[derive]. When derived on structs, it will produce a lexicographic ordering based on the top-to-bottom declaration order of the struct’s members. When derived on enums, variants are ordered by their top-to-bottom discriminant order.
当在结构上引入时,它将根据结构成员的自上而下的声明顺序生成字典顺序。在枚举上派生时,变体按其从上到下的判别顺序排序。
可用于指定Vector
中类型实例的排序规则
自定义实现时,只需要实现partial_cmp
方法
PartialOrd trait
是PartialEq trait
的子trait
,二者的实现需要保持一致PartialOrd trait
要求同时实现PartialEq trait
PartialOrd
改良了 PartialEq
,后者仅能比较是否相等,而前者除了能比较是否相等,还能比较孰大孰小。
如果特定类型的全部成员都实现了 PartialOrd
特性,那么该类型也可以衍生出该特性:
示例:使用浮点型来比较
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
}
}
Ord
定义
pub trait Ord: Eq + PartialOrd<Self> {
fn cmp(&self, other: &Self) -> Ordering;
// 提供默认实现
fn max(self, other: Self) -> Self { ... }
fn min(self, other: Self) -> Self { ... }
fn clamp(self, min: Self, max: Self) -> Self { ... }
}
特点
更加严格的顺序:对于任意 a
与 b
仅有一个为真: a < b
,a == b
,a > b
。
对NaN
有 NaN < 0 == false
与 NaN >= 0 == false
同时为真,不满足上面三个任一一个条件,所以不满足Ord trait
使用
可以使用#[derive(Eq)]
引入
This trait can be used with #[derive]. When derived on structs, it will produce a lexicographic ordering based on the top-to-bottom declaration order of the struct’s members. When derived on enums, variants are ordered by their top-to-bottom discriminant order.
当在结构上引入时,它将根据结构成员的自上而下的声明顺序生成字典顺序。在枚举上派生时,变体按其从上到下的判别顺序排序。
Ord requires that the type also be PartialOrd and Eq (which requires PartialEq).
需同时实现PartialOrd
和Eq
自定义实现时,只需要实现cmp
方法
对于实现了Ord
特性的类型,我们可以将它存储于BTreeMap
和BTreeSet
,并且可以通过sort()
方法对切片,或者任何可以自动解引用为切片的类型进行排序,例如Vec
和VecDeque
。
示例:同时实现PartialOrd
、PartialEq
、Ord
use std::cmp::Ordering;
#[derive(Eq)]
struct Person {
id: u32,
name: String,
height: u32,
}
impl PartialEq for Person {
fn eq(&self, other: &Self) -> bool {
self.height == other.height
}
}
impl PartialOrd for Person {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
// 最好是调用Ord的cmp方法进行比较
Some(self.cmp(other))
}
}
impl Ord for Person {
fn cmp(&self, other: &Self) -> Ordering {
self.height.cmp(&other.height)
}
}