Trait Description
Drop Destructors. Cleanup code that Rust runs automatically whenever a value is dropped.
Sized Marker trait for types with a fixed size known at compile time, as opposed to types (such as slices) that are dynamically sized.
Clone Types that support cloning values.
Copy Marker trait for types that can be cloned simply by making a byte-for-byte copy of the memory containing the value.
DerefandDerefMut Traits for smart pointer types.
Default Types that have a sensible “default value.”
AsRefandAsMut Conversion traits for borrowing one type of reference from another.
BorrowandBorrowMut Conversion traits, like AsRef/AsMut, but additionally guaranteeing consistent hashing, ordering, and equality.
FromandInto Conversion traits for transforming one type of value into another.
TryFromandTryInto Conversion traits for transforming one type of value into another, for transformations that might fail.
ToOwned Conversion trait for converting a reference to an owned value.

Drop

  1. trait Drop {
  2. fn drop(&mut self);
  3. }

drop都是隐式调用的,如果自己去掉会报错。如果实现了DropRust在drop值所包含的内存的时候会先执行实现的drop方法。
一般来说用户不用实现Drop,除非有一些Rust不知道怎么处理的资源需要释放。比如Rust的文件类型需要使用C的函数来释放

  1. impl Drop for FileDesc {
  2. fn drop(&mut self) {
  3. let _ = unsafe { libc::close(self.fd) };
  4. }
  5. }

Rust的prelude里有个一个drop函数可以用来drop一个值:fn drop<T>(_x: T) { }。但这个函数其实就是获取值的所有权,然后什么都不做。

Sized

一个Sized类型就是这个类型的值所占的内存大小永远是一样的。几乎所有的类型都是Sized,即便是Vec,因为他是一个指针加长度加容量,可变的只是堆上的buffer。
所有大小不变的类型都实现了Sized,而且Sized没有关联的方法,Rust自动为所有适用的类型实现了Sized。程序员自己并不能实现Sized。这个trait的唯一作用就是用作类型变量的bound。这样的trait叫marker trait(记号特性?)。

大小不确定的类型:

切片,比如str和数组切片[T]。平时用的都是&str和&[T],这两个都是指向切片的指针,而切片本身的大小是不确定的。
dyn,也就是trait object指向的值。因为实现了某个trait的可能是任何类型,所以大小是不能确定的。
因为unsized值是不能保存到变量里的,所以只能通过指针来使用他们。
因为Sized类型占绝多数,所以在声明类型参数的时候不用指明T: Sized。而当要使用unsized的类型的时候才需要注明:T: ?Sized。
一个struct的最后一个字段可以是?Sized,而且只有这个字段可以。如果有这样的字段,那这个struct就是一个?Sized。Rc的内部实现就有一个这样的struct,RcBox。这样的结构体无法直接创建,必须先创建一个Sized版的,然后赋给unsized版的引用类型

  1. struct Foo<T: ?Sized> {
  2. foo: T
  3. }
  4. fn main() {
  5. let foo_sized: Foo<[i8; 3]> = Foo { foo: [1,2 ,3] };
  6. let foo_unsized: &Foo<[i8]> = &foo_sized;
  7. for &i in foo_unsized.foo.iter() {
  8. println!("{}", i);
  9. }
  10. }

Clone

Clone表示类型可以复制。他的定义:

  1. trait Clone: Sized {
  2. fn clone(&self) -> Self;
  3. fn clone_from(&mut self, source: &Self) {
  4. *self = source.clone()
  5. }
  6. }

clone一个值会复制这个值的所有字段和其字段包含的值,所以是特别耗费时间和内存的,但RcArc除外,他们只是增加引用计数。
默认的clone_from会clone source然后赋给*self,但有的类型可能会优化,比如s = t.clone(),需要首先drop s的值然后申请内存复制一份t的值,然后把所有权给s。但如果用clone_from,而且s的capacity是足够容纳t的,那只需要将t的内容复制到s的buffer里然后修改len值就可以了,不用drop旧内存,申请新内存。
大多数标准库的类型是可以clone的,但有例外:std::sync::Mutex不能clone。std::fs::File有可能失败,所以std::fs::Filetry_clone会返回std::io::Result<File>

Copy

Copy是一个marker trait,是Clone的subtrait。对于编译器来说,Copy类型的值只需要一个shallow copy,所以拥有资源的类型是不能impl Copy的。
实现了Drop的类型都不能是Copy。对于Clone类型可以使用#[derive(Copy)]来实现Copy。

Deref和DerefMut

实现std::ops::Derefstd::ops::DerefMut让你可以自定义取值操作*.的行为。在引用那章说过的自动引用:String可以使用&str的方法,Vec<T>可以直接调用&[T]的方法等,是因为String实现了Deref<Target=str>Vec实现了Deref<Target=[T]>。这种行为叫deref coercions,一个方法的参数是某种类型T,如果deref操作能够将一个类型变成T,那Rust就会自动进行类型转换。
这两个trait的设计初衷是为了实现smart pointer,以及一些“拥有所有权的类型”但经常以引用的形式使用的类型,比如Vec和String。推荐不要把这两个trait用作自动获取目标类型的方法,像是C++那样子类自动获取父类方法的那样。一个应用的列子:

  1. struct Selector<T> {
  2. /// Elements available in this `Selector`.
  3. elements: Vec<T>,
  4. /// The index of the "current" element in `elements`. A `Selector`
  5. /// behaves like a pointer to the current element.
  6. current: usize
  7. }
  8. use std::ops::{Deref, DerefMut};
  9. impl<T> Deref for Selector<T> {
  10. type Target = T;
  11. fn deref(&self) -> &T {
  12. &self.elements[self.current]
  13. }
  14. }
  15. impl<T> DerefMut for Selector<T> {
  16. fn deref_mut(&mut self) -> &mut T {
  17. &mut self.elements[self.current]
  18. }
  19. }
  20. let mut s = Selector { elements: vec!['x', 'y', 'z'],
  21. current: 2 };
  22. // Because `Selector` implements `Deref`, we can use the `*` operator to
  23. // refer to its current element.
  24. assert_eq!(*s, 'z');
  25. // Assert that 'z' is alphabetic, using a method of `char` directly on a
  26. // `Selector`, via deref coercion.
  27. assert!(s.is_alphabetic());
  28. // Change the 'z' to a 'w', by assigning to the `Selector`'s referent.
  29. *s = 'w';
  30. assert_eq!(s.elements, ['x', 'y', 'w']);

但在泛型中类型bound中,Rust不会应用deref coercions:

  1. let s = Selector { elements: vec!["good", "bad", "ugly"],
  2. current: 2 };
  3. fn show_it(thing: &str) { println!("{}", thing); }
  4. show_it(&s);
  5. use std::fmt::Display;
  6. fn show_it_generic<T: Display>(thing: T) { println!("{}", thing); }
  7. show_it_generic(&s);

上面第二个函数调用show_it_generic(&s);会报错,因为Rust不在类型bound中应用deref coercions。但可以手动写明强制引用:

  1. show_it_generic(&s as &str);
  2. // 或者
  3. show_it_generic(&*s);

Default

一些类型有明显的默认值,比如数字的默认值可以是0,字符串的默认值是空串。这样的类型可以实现Default,用来返回默认值:

  1. trait Default {
  2. fn default() -> Self;
  3. }

常见用法:
Iterator的partition方法会按某个closure的输出来讲讲一个iterator的元素分到两个collection里。而目标collection的类型就需要实现Default,还需要实现Extend。partition的方法是先用default创建两个默认值,然后用Extend往创建的默认值里添加元素。

  1. use std::collections::HashSet;
  2. let squares = [4, 9, 16, 25, 36, 49, 64];
  3. let (powers_of_two, impure): (HashSet<i32>, HashSet<i32>)
  4. = squares.iter().partition(|&n| n & (n-1) == 0);
  5. assert_eq!(powers_of_two.len(), 3);
  6. assert_eq!(impure.len(), 4);
  7. let (upper, lower): (String, String)
  8. = "Great Teacher Onizuka".chars().partition(|&c| c.is_uppercase());
  9. assert_eq!(upper, "GTO");
  10. assert_eq!(lower, "reat eacher nizuka");

如果一个类型T实现了Default,则rust会自动为T的一些smart pointer类型实现Default。
如果tuple的所有元素类型都有Default,则该tuple就有Default。
struct不会自动实现Default,但如果struct的所有字段类型都有Default,则可以使用#[derive(Default)]实现Default。

AsRef和AsMut

  1. trait AsRef<T: ?Sized> {
  2. fn as_ref(&self) -> &T;
  3. }
  4. trait AsMut<T: ?Sized> {
  5. fn as_mut(&mut self) -> &mut T;
  6. }

这两个trait的功能是方便的从一个类型U中借用一个&T。比如打开文件的函数std::fs::File::open需要的参数是&Path,但传入String,&str等都可以,就是因为这些类型实现了AsRef,open函数在内部调用了as_ref方法获取了&Path。

blanket implementation:用泛型的方式给某些类型统一实现一个trait。

  1. impl<'a, T, U> AsRef<U> for &'a T
  2. where T: AsRef<U>,
  3. T: ?Sized, U: ?Sized
  4. {
  5. fn as_ref(&self) -> &U {
  6. (*self).as_ref()
  7. }
  8. }

实现了AsRef的不一定能应该实现AsMut,因为有些数据修改会破坏原数据的规则。比如String实现了AsRef<[u8]>。但却不能实现AsMut因为String是UTF-8编码的,如果变成&mut [u8]之后修改了某个字节,可能就不是一个正确的UTF-8编码了。

Borrow和BorrowMut

Borrow和AsRef基本相同,只是增加了一个限制:要求其实现返回的&T和原来的类型(这个类型不一定是T)能得到相同的hash和比较的结果。其定义和AsRef基本一样:

  1. trait Borrow<Borrowed: ?Sized> {
  2. fn borrow(&self) -> &Borrowed;
  3. }

Borrow的设计初衷是为了解决哈希表和其他类似的类型的一些情况:

  1. impl<K, V> HashMap<K, V> where K: Eq + Hash
  2. {
  3. fn get(&self, key: K) -> Option<&V> { ... }
  4. }

如果用key取value的方法定义是这样的,那在调用的时候还要创建一个K类型的值,然后转移到方法里用掉。如果吧K换成&K:

  1. impl<K, V> HashMap<K, V> where K: Eq + Hash
  2. {
  3. fn get(&self, key: &K) -> Option<&V> { ... }
  4. }

那在调用的时候:hashtable.get(&”twenty-two”.to_string())。要把”twenty-two”转换成String(申请heap内存),然后取该String的引用,传递给get,执行完get之后就drop掉。这样更荒谬。所以真实的方法是这样的:

  1. impl<K, V> HashMap<K, V> where K: Eq + Hash
  2. {
  3. fn get<Q: ?Sized>(&self, key: &Q) -> Option<&V>
  4. where K: Borrow<Q>,
  5. Q: Eq + Hash
  6. { ... }
  7. }

只要传入的值能borrow一个&Q就行,这样如果方法需要的是&str,那传入的变量可以是String,可以是字面量,不用重新创建值或者申请内存。标准库用blanket implementation给所有的类型都实现了T: Borrow,所以传递K可以得到一个&K。为了方便所有的&mut T 也都实现了Borrow,这样不用在有了&mut T的时候还要从原变量再借一个&T。

From和Into

和AsRef类似,AsRef是从原值创建一个引用,而Into是将原值转移返回一个新值,From是获取原值的所有权得到一个新值。

  1. trait Into<T>: Sized {
  2. fn into(self) -> T;
  3. }
  4. trait From<T>: Sized {
  5. fn from(other: T) -> Self;
  6. }

标准库自动给所有类型都实现了自己转换成自己的的From和Into。

From还起到了构建方法的作用。标准库自动的给实现了From的类型实现了对应的Into。所以如果自定义的类型有从一个参数构建实例的方法,最好用实现From的形式。

因为From和Into会获取所有权,所以生成的新值可能会使用原值的旧资源,这样可以提高性能。因为会获取所有权并转成其他类型,程序的一些类型限制会变得宽松,比如程序想把String当成普通的字节使用,把使用类型转换就让这样的操作变得可能。

这样的类型转换并不“便宜”,因为构建一个新的实例可能会申请内存,拷贝内存等。

?使用了From和Into,在需要的时候会把一个具体的错误类型变成一个更通用的类型。

TryFrom and TryInto

From和Into是不会出错的,Try*是可能出错的版本,会返回Result。
这样可以用Result的方法来处理意外或错误的情况,比如unwrap_or和unwrap_or_else。

ToOwned

  1. trait ToOwned {
  2. type Owned: Borrow<Self>;
  3. fn to_owned(&self) -> Self::Owned;
  4. }

和Clone类似,Clone只能复制一个类型到一个相同的类型,ToOwened可以复制一个类型去构建另外一个类型,只要这个类型实现了Borrow。

Cow

前面的Borrow或者ToOwned,要么获取一个引用要么获取一个值,在编译的时候就确定了。std::borrow::Cow可以在运行时决定是要获取引用还是值(clone on write)。
例子

  1. 根据错误类型返回不同的错误信息,有些错误是提前知道的返回的可以是字符串常量 &’static str,如果一些没有准备好的字符串,可以在运行时生成一个字符串String。
  2. 从Vec生成字符串,如果给的vec是正确的utf8,则只需要将原vec转换类型变成&str。不需要申请内存。而如果有无效的utf-8。则需要插入�。这是时候因为改变了原字符串的长度,需要重新申请内存,返回String。 ```rust // some bytes, in a vector let sparkle_heart = vec![240, 159, 146, 150];

let sparkle_heart = String::from_utf8_lossy(&sparkle_heart);

assert_eq!(“💖”, sparkle_heart);

  1. 可以看出Cow有个好处就是可以在需要的时候才申请内存。
  2. ```rust
  3. enum Cow<'a, B: ?Sized>
  4. where B: ToOwned
  5. {
  6. Borrowed(&'a B),
  7. Owned(<B as ToOwned>::Owned),
  8. }

为什么Owned的类型是<B as ToOwned>::Owned
参考上面ToOwen的关联类型是type Owned: Borrow<Self>,也就是说Owned是一个可以借出&B的类型,所以如果B是[u8],那Borrowed就是&[u8],Owned就是Vec[u8]。所以如果想让Cow返回一个字符串引用或者有heap内存的String,需要把Cow定义成:Cow<’a, str>。

Cow实现了Deref,所以可以直接调用B的方法。

虽然Borrowed的类型是一个不可变的引用(分享引用,&’a B),但如果需要一个可变的引用,可以调用Cow的to_mut方法生成一个可变的引用,这时候Cow会用Borrowed里的引用复制一个值出来,把自己变成Owned,然后再返回一个可变的引用,这也就是clone on write。Cow也有一个into_owned方法,可以直接把自己变成Owned那个类型本书,并把所有权转移给调用者。