1、Into 和 From
Into
std::convert
下面,有两个 Trait,Into/From
。它们的作用是配合泛型,进行一些设计上的归一化处理。
它们的基本形式为: From<T>
和 Into<T>
。
对于类型为 U
的对象 foo
,如果它实现了 From<T>
,那么,可以通过 let foo = U::from(bar)
来生成自己。这里,bar
是类型为 T
的对象。
示例, String
实现了 From<&str>
,所以 String
可以从 &str
生成:
let string = "hello".to_string();
let other_string = String::from("hello");
assert_eq!(string, other_string);
From
对于一个类型为 U: Into<T>
的对象 foo
,Into
提供了一个函数:.into(self) -> T
,调用 foo.into()
会消耗自己(转移资源所有权),生成类型为 T
的另一个新对象 bar
。
2、PartialEq & Eq
等于比较 ==
,两者都需要满足的条件有:
- 对称性(Symmetry):
a == b
可推出 b== a
- 传递性(Transitivity):
a == b
且b == c
可推出a == c
而 Eq 需要额外满足的条件有:
- 反身性:
a == a
PartialEq 可使用 #[derive]
来交由编译器实现,这样一个 struct 在进行相等比较时,会对其中每一个字段进行比较,如果遇到枚举,还会对枚举所拥有的数据进行比较。你也可以自己实现自己的 PartialEq 方法,例子如下:
#[derive(std::fmt::Debug)]
struct Book {
isbn: i32,
name: String,
}
impl PartialEq for Book {
fn eq(&self, other: &Self) -> bool {
self.isbn == other.isbn
}
}
fn main() {
let a = Book {
isbn: 112,
name: String::from("science"),
};
let b = Book {
isbn: 112,
name: String::from("science"),
};
println!("{:?}", a == b); // true
}
实现 Eq 的前提是已经实现了 PartialEq,对于上面的例子,可以使用 #[derive(Debug)]
或者 impl Eq for Book{}
即可。
如果你实现了PartialEq , 那么请你也尽可能一并实现 Eq,除非不允许(不能)! 对于浮点类型,Rust 只实现了 PartialEq 而不是 Eq,原因就是
NaN != NaN
。
3、Hash
散列值与相等的概念密切相关,因此如果实现自己的PartialEq Trait
,还应该实现Hash Trait
。
一个原则: if
x == y
thenhash(x) == hash(y)
将对FileInfo求哈希值委托给其成员 path:
impl Hash for FileInfo {
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.path.hash(hasher); //注意此处,hash a FileInfo就是hash其path。
}
}
这种委托技术适用于所有类型,因为标准库中的所有基本类型都实现了PartialEq
和 Hash
。
4、Ord & ParticalOrd
使用运算符<
、<=
、>=
和>
可以计算值的相对顺序,为此必须为自定义类型实现PartialOrd
。
类似于 Eq,Ord 指的是 Total Order,需要满足以下三个性质:
- 反对称性(Antisymmetry):
a <= b
且a >= b
可推出a == b
- 传递性(Transitivity):
a <= b
且b <= c
可推出a <= c
- 连通性(Connexity):
a <= b
或a >= b
而 PartialOrd 无需满足连通性,只满足反对称性和传递性即可。
- 反对称性:
a < b
则有!(a > b)
,反之亦然 - 传递性:
a < b
且b < c
可推出a < c
,==
和>
同理
Ord & PartialOrd 均可通过 #[derive]
交由编译器自动实现,当使用 #[derive]
实现后,将会基于 struct 的字段声明以字典序进行比较,遇到枚举中的数据也会以此类推。
依赖要求:
- PartialOrd 要求你的类型实现 PartialEq
- Ord 要求你的类型实现 PartialOrd 和 Eq(因此 PartialEq 也需要被实现)
示例:
impl PartialOrd for FileInfo {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.path.partial_cmp(&other.path)
}
}
其中 Ordering
是一个简单的枚举类型,有 3 个可能的值:
pub enum Ordering {
Less,
Equal,
Greater,
}
partial_cmp
这个方法返回值类型是个 Option,而非直接是一个Ordering
值类型。 (这仍然与浮点数类型有关, 因为NaN
不是一个可以表示的数值, 诸如:3.0 < NaN
这样的表达式毫无意义!对于这种情况,partial_cmp
就会返回None
因此浮点数是Rust标准库中发生此结果的唯一特例。)
impl Ord for FileInfo {
fn cmp(&self, other: &Self) -> Ordering {
self.path.cmp(&other.path) //将排序委托给其成员path。
}
}
5、AsRef 和 AsMut
http://web.mit.edu/rust-lang_v1.25/arch/amd64_ubuntu1404/share/doc/rust/html/book/first-edition/borrow-and-asref.htmlstd::convert
下面,还有另外两个 Trait,AsRef/AsMut
,它们功能是配合泛型,在执行引用操作的时候,进行自动类型转换。这能够使一些场景的代码实现得清晰漂亮,大家方便开发。
AsRef
AsRef
提供了一个方法 .as_ref()
。
对于一个类型为 T
的对象 foo
,如果 T
实现了 AsRef<U>
,那么,foo
可执行 .as_ref()
操作,即 foo.as_ref()
。操作的结果,我们得到了一个类型为 &U
的新引用。
注:
- 与
Into<T>
不同的是,AsRef<T>
只是类型转换,foo
对象本身没有被消耗; T: AsRef<U>
中的T
,可以接受 资源拥有者(owned)类型,共享引用(shared referrence)类型 ,可变引用(mutable referrence)类型。
示例:
fn is_hello<T: AsRef<str>>(s: T) {
assert_eq!("hello", s.as_ref());
}
let s = "hello";
is_hello(s);
let s = "hello".to_string();
is_hello(s);
因为
String
和&str
都实现了AsRef<str>
。
AsMut
AsMut<T>
提供了一个方法 .as_mut()
。它是 AsRef<T>
的可变(mutable)引用版本。
对于一个类型为 T
的对象 foo
,如果 T
实现了 AsMut<U>
,那么,foo
可执行 .as_mut()
操作,即 foo.as_mut()
。操作的结果,我们得到了一个类型为 &mut U
的可变(mutable)引用。
注:在转换的过程中,foo
会被可变(mutable)借用。
6、Borrow, BorrowMut, ToOwned
Borrow
use std::borrow::Borrow;
Borrow
提供了一个方法 .borrow()
。
对于一个类型为 T
的值 foo
,如果 T
实现了 Borrow<U>
,那么,foo
可执行 .borrow()
操作,即 foo.borrow()
。操作的结果,我们得到了一个类型为 &U
的新引用。Borrow
可以认为是 AsRef
的严格版本,它对普适引用操作的前后类型之间附加了一些其它限制。Borrow
的前后类型之间要求必须有内部等价性。不具有这个等价性的两个类型之间,不能实现 Borrow
。AsRef
更通用,更普遍,覆盖类型更多,是 Borrow
的超集。
use std::borrow::Borrow;
fn check<T: Borrow<str>>(s: T) {
assert_eq!("Hello", s.borrow());
}
let s = "Hello".to_string();
check(s);
let s = "Hello";
check(s);
BorrowMut
use std::borrow::BorrowMut;
BorrowMut<T>
提供了一个方法 .borrow_mut()
。它是 Borrow<T>
的可变(mutable)引用版本。
对于一个类型为 T
的值 foo
,如果 T
实现了 BorrowMut<U>
,那么,foo
可执行 .borrow_mut()
操作,即 foo.borrow_mut()
。操作的结果我们得到类型为 &mut U
的一个可变(mutable)引用。
注:在转换的过程中,foo
会被可变(mutable)借用。
ToOwned
use std::borrow::ToOwned;
ToOwned
为 Clone
的普适版本。它提供了 .to_owned()
方法,用于类型转换。
有些实现了 Clone
的类型 T
可以从引用状态实例 &T
通过 .clone()
方法,生成具有所有权的 T
的实例。但是它只能由 &T
生成 T
。而对于其它形式的引用,Clone
就无能为力了。
而 ToOwned
trait 能够从任意引用类型实例,生成具有所有权的类型实例。
Index
Error
实现 Error
trait,这样其他错误可以包裹这个错误类型。
Cow
Cow
是一个枚举类型,通过 use std::borrow::Cow;
引入。它的定义是 Clone-on-write
,即写时克隆。本质上是一个智能指针。
https://wiki.jikexueyuan.com/project/rust-primer/intoborrow/cow.html