特质trait

对未定义的Self定义的方法集,定义trait之后将其作用于impl块中。
对任何数据类型都可以实现trait。

派生

  • 通过#[derive]属性,编译器提供某些trait基本实现。更复杂的行为可以手动实现所需要的trait

使用dyn返回trait

  • Rust编译器决定了每个函数返回值需要多少空间,这意味着每个函数都必须返回一个具体类型。因此trait不能做为函数的返回值来使用,因为不同的实现造成了trait需要不同的内存量。
  • 因此使用Box返回包含一些trait的引用更为方便,因为引用的大小是已知的,使用引用指向已分配堆中的trait,这样就能返回trait了。
  • 返回指向堆的trait指针,需要使用关键字dyn来定义,如Box<dyn Animal>

运算符重载

  • Rust中很多运算符通过trait来重载。

Drop

  • Drop trait只有一个方法:drop,它会在对向离开作用域的时候自动调用该方法,它的作用是释放实现者的实例拥有的资源。
  • Box,Vec,String,File,以及 Process 是一些实现了 Drop trait 来释放 资源的类型。Drop trait 也可以为任何自定义数据类型手动实现。
  • 实现drop的类型可以自动销毁,你也可以手动调用drop函数将其销毁。

Iterator

  • 用来对集合(数组之类)实现迭代,只需要定义返回next元素的方法,可以手动在impl或者自动定义。
  • for结构使用.into_iter()方法将一些集合类型转换为迭代器。
  • iter中还有很多方法,具体可以查看cargo.io查看。

impl trait

impl trait在作为返回值使用的过程之中,可以简化函数定义,前提是一定要有已经分配好堆的trait,即这个trait已经被实现了。

Clone trait

  • clone trait会定义.clone()方法以供使用(在对于结构体等类型没有实现clone是无法使用的,结构体使用的格式是:#[derive(Clone)])
  • 相较于move方法,clone方法使用过程中能够更好的处理一些问题。

父trait

  • 可以为一个trait定义它的父trait,这样,在子trait中就可以使用父trait中的方法。

记得使用dyn方法进行描述。

消除重叠trait

  • 相较于之前的介绍,如果不同的trait在同一个结构体中有相同的实现方法,这样就必须使用如下的方法进行区分:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
trait UsernameWidget {
// 从这个 widget 中获取选定的用户名
fn get(&self) -> String;
}

trait AgeWidget {
// 从这个 widget 中获取选定的年龄
fn get(&self) -> u8;
}

// 同时具有 UsernameWidget 和 AgeWidget 的表单
struct Form {
username: String,
age: u8,
}

impl UsernameWidget for Form {
fn get(&self) -> String {
self.username.clone()
}
}

impl AgeWidget for Form {
fn get(&self) -> u8 {
self.age
}
}

fn main() {
let form = Form{
username: "rustacean".to_owned(),
age: 28,
};

// 如果取消注释此行,则会收到一条错误消息,提示 “multiple `get` found”(找到了多个`get`)。
// 因为毕竟有多个名为 `get` 的方法。
// println!("{}", form.get());

let username = <Form as UsernameWidget>::get(&form);
assert_eq!("rustacean".to_owned(), username);
let age = <Form as AgeWidget>::get(&form);
assert_eq!(28, age);
}