Rust Standard library trait
1. Generic Type vs Associated Type
1.1. Knowledge
Both generic types and associated types defer the decision to the implementer on which concrete types should be used in the trait’s functions and methods, so this section seeks to explain when to use one over the other.
The general rule-of-thumb is:
- Use associated types when there should only be a single impl of the trait per type.
- Use generic types when there can be many possible impls of the trait per type.
Let’s say we want to define a trait called Add
which allows us to add values together. Here’s an initial design and impl that only uses associated types:
1.2. Playground Code
2. SubTrait && SuperTrait
2.1. Knowledge
The “sub” in “subtrait” refers to subset and the “super” in “supertrait” refers to superset. If we have this trait declaration:
All of the types which impl Subtrait are a subset of all the types which impl Supertrait, or to put it in opposite but equivalent terms: all the types which impl Supertrait are a superset of all the types which impl Subtrait.
Also, the above is just syntax sugar for:
2.2. Comprehensive Infos
The relationship between subtraits and supertraits is: subtraits refine their supertraits. “Refinement” can mean different things in different contexts:
- a subtrait might make its supertrait’s methods’ impls more specialized, faster, use less memory, e.g. Copy: Clone
- a subtrait might make additional guarantees about the supertrait’s methods’ impls, e.g. Eq: PartialEq, Ord: PartialOrd, ExactSizeIterator: Iterator
- a subtrait might make the supertrait’s methods more flexible or easier to call, e.g. FnMut: FnOnce, Fn: FnMut
- a subtrait might extend a supertrait and add new methods, e.g. DoubleEndedIterator: Iterator, ExactSizeIterator: Iterator
2.3. Playground Code
3. PartialEq && Eq trait
3.1. Knowledge
这两个 traits 的名称实际上来自于抽象代数中的等价关系和局部等价关系。
3.2. 等价关系(equivalence relation)
设 R 是非空集合 A 上的二元关系,若 R 是自反的、对称的、传递的,则称 R 是 A 上的等价关系。
-
自反性(reflexivity):
∀ a ∈A, => (a, a) ∈ R
-
对称性(symmetry):
(a, b) ∈R∧ a ≠ b => (b, a)∈R
-
传递性(transitivity):
(a, b)∈R,(b, c)∈R =>(a, c)∈R
说人话版本:
-
自反性:满足
a==a
-
对称性:
if a==b than b==a
-
传递性:
if a==b && b==c than a==c
3.3. PartialEq
对应局部等价关系,只满足对称性和传递性,不满足自反性。
比如浮点数,NaN!=NaN
3.4. Eq
对应等价关系,满足 PartialEq
的同时满足 Eq
在 Rust 中,Eq
的实现实际上是空的(也叫 Marker Traits
),Trait Eq
是 Trait PartialEq
的 Subtrait
,Trait Eq
需要的 method
:fn eq(&self, other: &Self) -> bool
,已经在 Trait Partial
里实现了,声明 Eq
额外告诉编译器这个类型满足自反性这么个信息。
3.5. Marker Traits
3.6. Implement
3.7. Productive
手动实现 PartialEq
自动实现:PartialEq
If all the members of a type impl PartialEq
then it can be derived:
引用类型的比较也会被自动实现
3.8. Noted
原 Blog 里举得糟糕的扑克牌花色和大小的问题,也说明了 PartialEq
并不能理解为其内容里有一部分相等。本质上它应该是就是 Eq
,只是不满足自反性的 Eq。
胡乱的实现两个不同 type 的 PartialEq
最终会自相矛盾,扑克牌的例子用类似于 fn Card.is_suit(shade: Shade) -> bool
的 methods
,会合理的多。
4. PartialOrd && Ord trait
4.1. PartialOrd
PartialOrd
is a subtrait of PartialEq
and their impls must always agree with each other.
The lt
, le
, gt
, and ge
methods of this trait can be called using the <
, <=
, >
, and >=
operators, respectively.
All PartialOrd
impls must ensure that comparisons are transitive and duality. That means for all a, b, and c:
-
transitive:
a < b
andb < c
impliesa < c
. The same must hold for both==
and>
. -
duality:
a < b
if and only ifb > a
.
impl a PartialOrd
manually
If all the members of a type impl PartialOrd
then it can be derived:
4.2. Ord
Ord
is a subtrait of Eq
and PartialOrd<Self>
:
use #[derive(...)]
if members of a type impl that trait.
impl a Ord
manually:
需要注意的就是,要么都用 derive 宏的写法,要么都手动实现
4.3. Noted
subtrait
和 面向对象语言里的继承,思维方式是不一致的。上面这个手动实现 Ord trait
的代码,就没法用继承的逻辑来看。
-
Ord : Eq + PartialOrd<Self>
: 理解为Ord
refine(完善了)Eq
+PartialOrd
-
但是一旦写完了
imp Ord for Point
,虽然还没有PartialOrd/Eq/PartialEq
,但是剩下的 trait 可以用Ord
的 methods 来判断了… - 强行用继承的思维去理解就是父类方法的实现调用了子类方法??? 所以不能这么想…
5. Index && IndexMut
5.1. Index && IndexMut trait
可以用[]
来对实现了 Index<T, Output = U>
的类型进行取值为 T
的运算,返回&U
类型,编译器会自动加上解引用运算符*
,需要注意的语法糖
More: []
里可以用 Range<uszie>
索引来得到切片。
MORE: 通过手动实现 Index trait
,可以把 Index<T, Output = U>
里的 T
改成我们想用的任意类型,这样就可以使用[]
运算符来取值。
6. From && Into
6.1. From trait
6.2. 使用 From trait 简化构造
- More
7. Error
7.1. Error trait
fn source(&self) -> Option<&(dyn Error + 'static)>;
-
默认实现是空的
None
,有需要的话可以覆写加上自己的实现 -
source
的意思是The lower-level source of this error, if any.
fn backtrace(&self) -> Option<&Backtrace>;
fn description(&self) -> &str;
、fn cause(&self) -> Option<&dyn Error>;
两个废弃接口,分别被 Display trait
和 fn source
取代了
8. TryFrom
8.1. TryFrom trait
8.2. Playground Code
9. FromStr
9.1. FromStr trait
-
trait From
的限定加强版,可能失败的转换 -
trait TryFrom
的 str 限定版本,等同于TryFrom<&str>
9.2. Playground Code
10. Iterator
10.1. Iterator trait
即使是对同一个类型实现迭代器,可以通过给返回的对象 Item
加上不同的限定符来实现返回不可变引用、可变引用、值,for example: Vec
:
Vec<T> 方法 |
返回类型 |
---|---|
.iter() |
Iterator<Item = &T> |
.iter_mut() |
Iterator<Item = &mut T> |
.into_iter() |
Iterator<Item = T> |
10.2. 任意迭代器的可变引用也是迭代器
有点绕口…,标准库里有这么个泛型实现:
就是说,一个迭代器的可变引用,可以被当作迭代器使用。比如 Iterator::take()
10.3. 什么都可以是迭代器
Iterator
是一个 trait
标准库:
11. IntoIterator
11.1. IntoIterator trait
-
IntoIterator
types can be converted into iterators -
for-in
loop will callinto_iter
method: