概念

_trait_告诉Rust编译器某个特定类型拥有可能与其他类型共享的功能。可以通过 trait 以一种抽象的方式定义共享的行为。可以使用_trait bounds_ 指定泛型是任何拥有特定行为的类型。

Traits are kind of similar to interfaces in OOP languages. They are used to define the functionality a type must provide. Multiple traits can be implemented for a single type.

trait相当于OOP语言中的接口,被用来定义类型必须要具备的功能(方法),可以在单个类型上实现多个**trait**

But traits can also include default implementations of methods. Default methods can be overridden when implementing types.

trait可以有方法的默认实现,为类型实现trait时能重写方法

Other than functions, traits can contain constants and types.

trait中还可以包含常量和类型(type 关键字)

And also in Rust, new traits can be implemented for existing types even for types like i8, f64 and etc. Same way existing traits can be implemented for new types you are creating. But we can not implement existing traits into existing types.

  • 可以为原有类型实现自定义**trait**
  • 可以为自定义类型实现内置**trait**
  • 不能原有类型实现内置**trait**,即不能重写为原有类型实现的内置trait

这个限制是被称为 相干性coherence) 的程序属性的一部分,或者更具体的说是 孤儿规则orphan rule),其得名于不存在父类型。这条规则确保了其他人编写的代码不会破坏你代码,反之亦然。没有这条规则的话,两个 crate 可以分别对相同类型实现相同的 trait,而 Rust 将无从得知应该使用哪一个实现。

为类型实现(impl)trait

使用impl关键字为类型实现trait
示例:

  1. struct Player {
  2. first_name: String,
  3. last_name: String,
  4. }
  5. trait FullName {
  6. fn full_name(&self) -> String;
  7. }
  8. impl FullName for Player {
  9. fn full_name(&self) -> String {
  10. format!("{} {}", self.first_name, self.last_name)
  11. }
  12. }
  13. fn main() {
  14. let player_2 = Player {
  15. first_name: "Roger".to_string(),
  16. last_name: "Federer".to_string(),
  17. };
  18. println!("Player 02: {}", player_2.full_name());
  19. }

trait的继承(inheritance)

  • 使用:指定父trait
  • 使用+指定多个trait

示例:

  1. trait Person {
  2. fn full_name(&self) -> String;
  3. }
  4. trait Employee : Person { // 指定父trait
  5. fn job_title(&self) -> String;
  6. }
  7. trait ExpatEmployee : Employee + Expat { // 指定多个父trait
  8. fn additional_tax(&self) -> f64;
  9. }

trait bound

多态概念(polymorphic)

In computing, static dispatch is a form of polymorphism fully resolved during compile time. It is a form of method dispatch, which describes how a language or environment will select which implementation of a method or function to use.

在计算中,静态分派是一种在编译时完全解决多态形式。它是方法分派的一种形式,它描述了语言或环境将如何选择要使用的方法或函数的实现

While Rust favors static dispatch, it also supports dynamic dispatch through a mechanism called ‘trait objects.’

Rust喜欢静态分派,但是它也支持通过“**trait**对象(猜测可能是运行时的一个对象,通过链式查找到具体实例的方法实现)”机制实现动态分派

Dynamic dispatch is the process of selecting which implementation of a polymorphic operation (method or function) to call at run time.

动态分派是指在运行时选择调用哪一个多态操作(方法或者函数)的实现的过程

trait bound

示例:

  1. trait GetSound {
  2. fn get_sound(&self) -> String;
  3. }
  4. struct Cat {
  5. sound: String,
  6. }
  7. impl GetSound for Cat {
  8. fn get_sound(&self) -> String {
  9. self.sound.clone()
  10. }
  11. }
  12. struct Bell {
  13. sound: String,
  14. }
  15. impl GetSound for Bell {
  16. fn get_sound(&self) -> String {
  17. self.sound.clone()
  18. }
  19. }
  20. // 指定为GetSound类型
  21. fn make_sound<T: GetSound>(t: &T) {
  22. println!("{}!", t.get_sound())
  23. }
  24. // 第二种写法,是第一种写法的语法糖
  25. fn make_sound(t: impl GetSound) {
  26. println!("{}!", t.get_sound())
  27. }
  28. fn main() {
  29. let kitty = Cat { sound: "Meow".to_string() };
  30. let the_bell = Bell { sound: "Ding Dong".to_string() };
  31. // 多态
  32. make_sound(&kitty); // Meow!
  33. make_sound(&the_bell); // Ding Dong!
  34. }

多个trait bound

可以通过 + 指定多个trait bound

  • pub fn notify(item: **impl** **Summary + Display**) { }
  • pub fn notify<**T**: **Summary + Display**>(item: **T**) {

通过 where 简化 trait bound

对于有多个泛型的函数,可以使用where从句指定trait bound的语法

示例:
原写法:
fn some_function<T: Display + Clone, U: Clone + Debug>(t: T, u: U) -> i32 { }
新写法:
fn some_function<T, U>(t: T, u: U) -> i32
where T: Display + Clone,
U: Clone + Debug
{ }

trait bound作为返回值

可以将**impl Summary**放在返回值的位置,作为对返回值的类型,不过并不常用,因为方法体只能返回一种具体类型,否则会报错。

这在闭包迭代器场景十分的有用。闭包和迭代器创建只有编译器知道的类型,或者是非常非常长的类型。impl Trait允许你简单的指定函数返回一个 Iterator无需写出实际的冗长的类型。

  1. fn returns_summarizable() -> impl Summary {
  2. Tweet {
  3. username: String::from("horse_ebooks"),
  4. content: String::from("of course, as you probably already know, people"),
  5. reply: false,
  6. retweet: false,
  7. }
  8. }

为泛型T添加trait bound条件

在使用implstruct实现方法时,可以为struct的泛型T添加trait bound,对于一个struct实例,其泛型T对应的具体类型需要满足trait bound条件。

因为创建struct实例时,需要指定泛型T具体是哪个类型,所以针对**T**的不同,**T**所实现的**trait**不同,实例所能调用的方法也有限制。

示例:

  1. use std::fmt::Display;
  2. struct Pair<T> {
  3. x: T,
  4. y: T,
  5. }
  6. // 对任何 T 类型有效
  7. impl<T> Pair<T> {
  8. fn new(x: T, y: T) -> Self {
  9. Self {
  10. x,
  11. y,
  12. }
  13. }
  14. }
  15. // 对泛型加trait bound条件
  16. // 只对实现了 Display 和 PartialOrd 两个 trait 的 T 类型有效
  17. impl<T: Display + PartialOrd> Pair<T> {
  18. fn cmp_display(&self) {
  19. if self.x >= self.y {
  20. println!("The largest member is x = {}", self.x);
  21. } else {
  22. println!("The largest member is y = {}", self.y);
  23. }
  24. }
  25. }