什么是trait?

Trait其实就是告诉编译器:

  • 某种类型具有哪些行为,并且这些行为其他的类型也可以拥有(类似于接口interface)
  • trait是一个公共行为的签名集合,也就是只声明有哪种行为,不做具体实现
  • 利用关键字trait就可以声明一个Trait ```rust pub trait Summary { // trait实现的只是一个签名的集合,并不会有具体的实现 fn summarize(&self) -> String
    }

pub struct NewsArticle { pub headline: String, pub location: String, pub author: String, pub content: String, }

pub struct Tweet { pub username: String, pub content: String, pub reply: bool, pub retweet: bool, }

// 为结构体实现trait,这里就是trait中签名的具体实现 impl Summary for NewsArticle { fn summarize(&self) -> String { format!(“{},作者{}({})”, self.headline, self.author, self.location) } }

impl Summary for Tweet { fn summarize(&self) -> String { format!(“{},用户名:{}”, self.content, self.username) }

}

  1. 从上面的例子可以看出:
  2. - Summary作为一个trait,定义了一个行为summarize
  3. - 这个行为结构体NesArticleTweet都可以拥有
  4. <a name="daYqU"></a>
  5. ## 默认行为
  6. - 默认行为:我们在定义trait的签名时,是可以给一个默认实现的
  7. - 默认实现中可以调用当前trait中的方法,即使这个方法是没有默认实现的
  8. - 默认行为可以通过在具体类型中实现trait时给重写掉
  9. 基于上面的例子我们来看下
  10. ```rust
  11. pub trait Summary {
  12. fn get_user(&self) -> String;
  13. // 这里就是默认实现,并且调用了自身的get_user这个方法,并且这个方法没有默认实现
  14. fn summarize(&self) -> String {
  15. format!("这是默认实现的trait,详情查看{}", self.get_user())
  16. }
  17. }
  18. pub struct NewsArticle {
  19. pub headline: String,
  20. pub location: String,
  21. pub author: String,
  22. pub content: String,
  23. }
  24. pub struct Tweet {
  25. pub username: String,
  26. pub content: String,
  27. pub reply: bool,
  28. pub retweet: bool,
  29. }
  30. // 为结构体实现trait,这里就是trait中签名的具体实现
  31. impl Summary for NewsArticle {
  32. fn summarize(&self) -> String {
  33. format!("{},作者{}({})", self.headline, self.author, self.location)
  34. }
  35. fn get_user(&self) -> String {
  36. format!("{}", self.author)
  37. }
  38. }
  39. impl Summary for Tweet {
  40. // fn summarize(&self) -> String {
  41. // format!("{},用户名:{}", self.content, self.username)
  42. // }
  43. fn get_user(&self) -> String {
  44. format!("{}", self.username)
  45. }
  46. }
  47. let tweet = Tweet {
  48. username: String::from("horse_ebooks"),
  49. content: String::from("of course, as you probably already know, people"),
  50. reply: false,
  51. retweet: false,
  52. };
  53. // 调用该trait的方法
  54. println!("1 new tweet: {}", tweet.summarize());

trait作为参数

trait作为参数的意思就是:
这个参数是实现了某个trait的这样一个结构

trait作为参数有两种方式:

  1. impl XXX
  2. Trait bound

我们基于上面的例子继续来解读一下:

我们需要实现一个notify方法,这个方法的参数item需要是实现了Summary这个trait的结构

impl trait

  1. // 第一种方法
  2. pub fn notify(item: impl Summary) {
  3. print!("爆炸新闻来袭!{}", item.summarize());
  4. }

Trait bound

  • Trait bounds:可以将泛型的类型绑定为某些trait
    // 将类型T绑定为实现了 Summary 这个trait的这样一个类型 
    pub fn notify<T: Summary>(item: T) { 
      print!("爆炸新闻来袭!{}", item.summarize()); 
    }
    

    利用+来绑定多来trait

如果现在的需求是一个类型是Summary和Copy这两个trait实现的类型呢?

pub fn notify<T: Summary + Copy>(item: T) { 
    print!("爆炸新闻来袭!{}", item.summarize()); 
}

当然这样如果是trait过多的话,就会造成函数的签名看起来很繁琐,所以rust提供了一个where给我们使用。

使用where简化多个绑定

where写在返回值类型的后面即可

pub fn notify<T, U>(item: T, item2: U) 
where  
    T: Summary + Copy, 
    U: xxxx + yyyy, 
{ 
    print!("爆炸新闻来袭!{}", item.summarize()); 
}

总结

image.png