1. Eq & PartialEq
-
符号:==、!=
-
区别:Eq 相比于 PartialEq 还需额外满足反身性,即
a == a。对于浮点类型,Rust 只实现了 PartialEq 而不是
Eq,原因就是 NaN != NaN。
PartialEq
可以直接使用 #[derive(PartialEq)] 派生宏交由编译器实现,对于 struct,Rust 会逐字段比较,对于 enum,会对 enum 中的数据进行比较。我们也可以自己实现该 trait。
struct Book {
name: String,
price: usize,
}
impl PartialEq for Book {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
}
}
Eq
实现 Eq 的前提是已经实现了 PartialEq,如果已经实现了 PartialEq,可以直接使用 #[derive(Eq)],但是当 struct 或者 enum 中存在浮点数时,我们需要手动实现该 trait。
#[derive(PartialEq)]
struct Book {
name: String,
price: f32
}
impl Eq for Book {} // 不用怀疑,就是这么简单~
2.Ord & PartialOrd
- 符号:<、⇐、>、>=
- 区别:PartialOrd 不许满足连通性(a⇐b或a>=b),只需满足反对称性(a ⇐ b 且 a >= b 可推出 a == b)和传递性(a ⇐ b 且 b ⇐ c 可推出 a ⇐ c)。
- 完成 PartialOrd 后会为你的类型提供lt()、le()、gt()、ge()的比较操作。
- 完成 Ord 后会为你的类型提供 max()、min() 和 clamp()(比较该值是否在某个区间内)。
PartialOrd
Ord & PartialOrd 均可通过 #[derive] 派生宏交由编译器自动实现,当使用派生宏实现后,将会基于 struct 的字段声明以字典序进行比较,遇到枚举中的数据也会以此类推。注意,实现 PartialOrd 要求该类型实现 PartialEq。
struct Person {
name: String,
age: usize,
}
impl PartialEq for Person {
fn eq(&self, other: &Self) -> bool {
self.age == other.age
}
}
impl PartialOrd for Person {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.age.partial_cmp(&other.age)
}
}
Ord
Ord 要求你的类型实现 PartialOrd 和 Eq(因此 PartialEq也需要被实现),实现 PartialEq,PartialOrd 以及 Ord 时要特别注意彼此之间不能有冲突(即比较的内容要一致)。浮点型实现了PartialOrd但是没有实现Ord,因为NaN < 0 == false和NaN >= 0 == false都为真。
/* ... */
impl Eq for Person {}
impl Ord for Person {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.age.cmp(&other.age)
}
}
3.From & Into
Rust 中的 as可以用于一些基本类型的简单转换,如果涉及到自定义类型的转换,我们可以为它们实现From 和 Into trait 以更方便地转换类型。
定义如下两个结构体:
struct Dog {
weight: usize,
height: usize,
}
struct People {
iq: isize,
eq: isize,
}
From
实现了 From trait 后会为你的类型提供 from()、into()、try_from() 和 try_into() 方法,使用 try_from() 和try_into() 方法始终是正确的,使用 into() 和 try_into()方法时要写上类型注解,否则 Rust 不知道你想转换成什么类型。
impl From<Dog> for People {
fn from(d: Dog) -> Self {
People {
iq: d.weight as isize + 10,
eq: -1 * d.height as isize,
}
}
}
fn main() {
let d1 = Dog {weight: 20, height: 20};
let d2 = Dog {weight: 30, height: 30};
let p1 = People::from(d);
let p2: People = d2.into();
}
Into
如果你想将 Dog 转为 People 类型,只实现 From 或者 Into trait 即可,否则会造成矛盾。
impl Into<People> for Dog {
fn into(self) -> People {
People {
iq: self.weight as isize + 10,
eq: -1 * self.height as isize,
}
}
}
fn main() {
// ... 和from一样的用法
}
4.TryFrom & TryInto
这一组 trait 作用和上一组相同,区别是这一组通常用于转换容易出错的类型。
TryFrom
实现了 TryFrom trait 后会自动为你的类型实现 try_from()、try_into()。
impl TryFrom<Dog> for People {
type Error = String;
fn try_from(value: Dog) -> Result<Self, Self::Error> {
if value.height < 0 as usize || value.weight < 0 as usize {
Err(String::from("转换出错 => Dog to People"))
} else {
Ok(People {
iq: value.weight as isize,
eq: value.height as isize,
})
}
}
}
fn main() {
let d1 = Dog{weight: 20, height: 20};
let d2 = Dog{weight: 20, height: 20};
let p1 = People::try_from(d).unwrap();
let p2 = d2.try_into().unwrap();
}
TryInto
实现了 TryInto trait 后会自动为你的类型提供 try_into() 方法。
impl TryInto<People> for Dog {
type Error = ();
fn try_into(self) -> Result<People, Self::Error> {
Err(())
}
}
fn main() {
let d = Dog {
weight: 20,
height: 20,
};
let p: People = d.try_into().unwrap();
}
5.ToString & FromStr
ToString
要把任何类型转换成 String,只需要实现那个类型的 ToString trait。
然而不要直接这么做,您应该实现 fmt::Display trait,它会自动提供 ToString,并且还可以用来打印类型。
use std::fmt;
struct Circle {
radius: i32
}
impl fmt::Display for Circle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Circle of radius {}", self.radius)
}
}
// ToString版本实现
impl ToString for Circle {
fn to_string(&self) -> String {
format!("Circle of radius {:?}", self.radius)
}
}
fn main() {
let circle = Circle { radius: 6 };
println!("{}", circle.to_string());
}
FromStr
#[derive(Debug)]
struct People {
name: String,
age: usize,
}
impl std::str::FromStr for People {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let ss: Vec<&str> = s.split(";").collect();
if ss.len() == 2 {
match ss[1].parse::<usize>() {
| Ok(age) => Ok(People {
name: ss[0].to_string(),
age: age,
}),
| Err(e) => Err(()),
}
} else {
Err(())
}
}
}
fn main() {
let s = "Mr.Li;20";
println!("{:?}", s.parse::<People>()); // Ok(People { name: "Mr.Li", age: 20 })
}