trait

  • trait 告诉 Rust 编译器:
    • 某种类型具有哪些并且可以与其他类型共享的功能
  • trait:抽象的定义共享行为
  • trait bounds(约束): 泛型类型参数 指定为实现了特定行为的类型

    要求泛型类型参数,实现了特定的 trait

  • trait 与其他语言的接口 (interface) 类似,但有些区别

    定义一个 trait

    类型它的行为由该类型本身可调用的方法来组成的, 但是有时候在不同类型上,它们都具有相同的方法, 这个时候,我们就称这个类型共享了相同的行为, 而 trait 就提供了一种方式,它可以把一些方法放到一起, 从而定义实现某种目的所必须的一种行为。

  • trait 定义:把方法签名放在一起,来定义实现其某种目的所必需的一组行为。

    • 关键字:trait
    • 只要方法签名,没有具体实现
    • trait 可以有多个方法:每个方法签名占一行,以;结尾
    • 实现该 trait 的类型必须提供具体的方法实现 ```rust pub trait Summary { // 具体名称 fn summarize(&self) -> String; // 方法签名的名称,参数和返回类型 } // 没有方法体,没有具体实现

fn main() {}

  1. <a name="EgDU0"></a>
  2. ### 在类型上实现 trait
  3. - 与为类型实现方法类似
  4. - 不同之处:
  5. - `impl `**`Xxxx for`**` Tweet {...}`
  6. - 在 impl 的块里,需要对 trait 里的方法签名进行具体的实现(不是很绝对)
  7. ```rust
  8. pub trait Summary {
  9. fn summarize(&self) -> String;
  10. }
  11. pub struct NewsArticle {
  12. pub headline: String,
  13. pub location: String,
  14. pub author: String,
  15. pub content: String,
  16. }
  17. // 为类型实现方法 impl NewsArticle
  18. // 为类型实现 trait impl Xxxx for NewsArticle
  19. impl Summary for NewsArticle {
  20. fn summarize(&self) -> String {
  21. format!("{}, by {} ({})", self.headline, self.author, self.location)
  22. }
  23. }
  24. pub struct Tweet {
  25. pub username: String,
  26. pub content: String,
  27. pub reply: bool,
  28. pub retweet: bool,
  29. }
  30. impl Summary for Tweet {
  31. fn summarize(&self) -> String {
  32. format!("{}: {}", self.username, self.content)
  33. }
  34. }
  1. use fanxing::Summary;
  2. use fanxing::Tweet;
  3. fn main() {
  4. let tweet = Tweet {
  5. username: String::from("horse_ebooks"),
  6. content: String::from("of course, as you probably"),
  7. reply: false,
  8. retweet: false,
  9. };
  10. println!("1 new tweet: {}", tweet.summarize());
  11. }

实现 trait 的约束

  • 可以在某个类型上实现某个 trait 的前提条件是:
    • 这个类型或这个 trait 是在本地 crate 里定义的
  • 无法为外部类型来实现外部的 trait:
    • 这个限制是程序属性的一部分(也就是一致性)
    • 更具体地说是孤儿规则:之所以这样命名是因为父类型不存在
    • 此规则确保其他人的代码不能破坏你的代码,反之亦然
    • 如果没有这个规则,两个 crate 可以为同一类型实现同一个 trait,Rust 就不知道应该使用哪个实现了。

      默认实现

      为 trait 中的某些或所有方法提供默认行为是非常有用的, 它可以使我们无需为每个类型的实现都提供自定义的行为, 我们可以针对某些特定的类型实现 trait 里的方法。 当我们为某个特定类型实现 trait 时,我们可以选择保留或者重载每个方法的默认实现。

  1. pub trait Summary {
  2. // fn summarize(&self) -> String;
  3. fn summarize(&self) -> String {
  4. String::from("(Read more...)") // 默认实现
  5. }
  6. }
  7. pub struct NewsArticle {
  8. pub headline: String,
  9. pub location: String,
  10. pub author: String,
  11. pub content: String,
  12. }
  13. // 为类型实现方法 impl NewsArticle
  14. // 为类型实现 trait impl Xxxx for NewsArticle
  15. impl Summary for NewsArticle {
  16. // fn summarize(&self) -> String {
  17. // format!("{}, by {} ({})", self.headline, self.author, self.location)
  18. // }
  19. }
  20. pub struct Tweet {
  21. pub username: String,
  22. pub content: String,
  23. pub reply: bool,
  24. pub retweet: bool,
  25. }
  26. impl Summary for Tweet {
  27. fn summarize(&self) -> String { // 默认实现的重写
  28. format!("{}: {}", self.username, self.content)
  29. }
  30. }
  1. use fanxing::Tweet;
  2. use fanxing::{NewsArticle, Summary};
  3. fn main() {
  4. let article = NewsArticle {
  5. headline: String::from("hahaaha"),
  6. content: String::from("The lalalalallalal"),
  7. author: String::from("iiii"),
  8. location: String::from("eeee"),
  9. };
  10. println!("1 news article: {}", article.summarize());
  11. let tweet = Tweet {
  12. username: String::from("horse_ebooks"),
  13. content: String::from("of course, as you probably"),
  14. reply: false,
  15. retweet: false,
  16. };
  17. println!("1 new tweet: {}", tweet.summarize());
  18. }
  • 默认实现的方法可以调用 trait 中其他的方法,即使这些方法没有默认实现。 ```rust pub trait Summary { fn summarize_author(&self) -> String; fn summarize(&self) -> String {
    1. format!("(Read more from {} ...)", self.summarize_author())
    } }

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

impl Summary for NewsArticle { fn summarize_author(&self) -> String { format!(“@{}”, self.author) } }

  1. ```rust
  2. use fanxing::{NewsArticle, Summary};
  3. fn main() {
  4. let article = NewsArticle {
  5. headline: String::from("hahaaha"),
  6. content: String::from("The lalalalallalal"),
  7. author: String::from("iiii"),
  8. location: String::from("eeee"),
  9. };
  10. println!("1 news article: {}", article.summarize());
  11. }
  • 注意:无法从方法的重写实现里面调用默认的实现。

    trait 作为参数

  • impl trait 语法:适用于简单情况

    1. pub fn notify(item: impl Summary) -> String {
    2. format!("Breaking news! {}", item.summarize())
    3. }
  • trait bound 语法:可用于复杂情况

    1. pub fn notify<T: Summary>(item: T) -> String {
    2. format!("Breaking news! {}", item.summarize())
    3. }

    两者相比 trait bound 简洁一些 ```rust pub fn notify1(item1: impl Summary, item2: impl Summary) -> String { format!(“Breaking news! {}”, item1.summarize()) }

pub fn notify(item1: T, item2: T) -> String { format!(“Breaking news! {}”, item1.summarize()) }

  1. - impl trait 语法是 trait bound 的语法糖
  2. - 使用 + 指定多个 trait bound
  3. ```rust
  4. use std::fmt::Display;
  5. pub fn notify1(item1: impl Summary + Display) -> String {
  6. format!("Breaking news! {}", item1.summarize())
  7. }
  8. pub fn notify<T: Summary + Display>(item1: T) -> String {
  9. format!("Breaking news! {}", item1.summarize())
  10. }
  • trait bound 使用 where 子句
    • 在方法签名后指定 where 子句
      1. pub fn notify<T: Summary + Display, U: Clone + Debug>(a: T, b: U) -> String {
      2. format!("Breaking news! {}", a.summarize())
      3. }

      函数名和参数列表之间有很长的 trait 约束信息,所以函数签名不直观, 这个时候就可以使用 where 子句来简化 trait 约束

  1. pub fn notify<T, U>(a: T, b: U) -> String
  2. where
  3. T: Summary + Display,
  4. U: Clone + Debug,
  5. {
  6. format!("Breaking news! {}", a.summarize())
  7. }

实现 trait 作为返回类型

  1. pub fn notify(s: &str) -> impl Summary {
  2. NewsArticle {
  3. headline: String::from("horse_ebooks"),
  4. content: String::from("of course, as you probably"),
  5. author: String::from("custer"),
  6. location: String::from("shanghai"),
  7. }
  8. }

返回类型不只一个就不行,尽管都实现了 trait

  1. pub fn notify(flag: bool) -> impl Summary {
  2. if flag {
  3. NewsArticle {
  4. headline: String::from("horse_ebooks"),
  5. content: String::from("of course, as you probably"),
  6. author: String::from("custer"),
  7. location: String::from("shanghai"),
  8. }
  9. } else {
  10. Tweet {
  11. username: String::from("horse_ebooks"),
  12. content: String::from("of course, as you probably"),
  13. reply: false,
  14. retweet: false,
  15. }
  16. }
  17. }
  • 注意:impl trait 只能返回确定的同一种类型,返回可能不同类型的代码会报错

    使用 trait bound 的例子

  • 使用 trait bound 修复 largest 函数 ```rust fn largest(list: &[T]) -> T { let mut largest = list[0].clone(); for item in list.iter() {

    1. if item > &largest {
    2. largest = item.clone();
    3. }

    } largest }

fn main() { let number_list = vec![34, 50, 25, 100, 65]; let result = largest(&number_list); println!(“The largest number is {}”, result);

  1. let char_list = vec!['y', 'm', 'a', 'q'];
  2. let result = largest(&char_list);
  3. println!("The largest char is {}", result);
  4. let str_list = vec![String::from("hello"), String::from("world")];
  5. let result = largest(&str_list);
  6. println!("The largest word is {}", result);

}

  1. 或者直接返回 T 的引用
  2. ```rust
  3. fn largest<T: PartialOrd + Clone>(list: &[T]) -> &T {
  4. let mut largest = &list[0];
  5. for item in list.iter() {
  6. if item > &largest {
  7. largest = item;
  8. }
  9. }
  10. largest
  11. }
  12. fn main() {
  13. let number_list = vec![34, 50, 25, 100, 65];
  14. let result = largest(&number_list);
  15. println!("The largest number is {}", result);
  16. let char_list = vec!['y', 'm', 'a', 'q'];
  17. let result = largest(&char_list);
  18. println!("The largest char is {}", result);
  19. let str_list = vec![String::from("hello"), String::from("world")];
  20. let result = largest(&str_list);
  21. println!("The largest word is {}", result);
  22. }

使用 trait bound 有条件的实现方法

  • 在使用泛型类型参数的 impl 块上使用 trait bound,我们可以有条件的为实现了特定 trait 的类型来实现方法 ```rust use std::fmt::Display;

struct Pair { x: T, y: T, }

impl Pair { fn new(x: T, y: T) -> Self { Self { x, y } } }

impl Pair { fn cmp_display(&self) { if self.x >= self.y { println!(“The largest member is x = {}”, self.x); } else { println!(“The largest member is y = {}”, self.y); } } }

  1. - 也可以为实现了其他 trait 的任意类型有条件的实现某个 trait
  2. - 为满足 trait bound 的所有类型上实现 trait 叫做覆盖实现(blanket implementations)
  3. 查看源代码 [https://github.com/rust-lang/rust/blob/142c831861ba5a995fd9de99198e7f6074b6b400/library/alloc/src/string.rs#L2267](https://github.com/rust-lang/rust/blob/142c831861ba5a995fd9de99198e7f6074b6b400/library/alloc/src/string.rs#L2267)
  4. ```rust
  5. /// A trait for converting a value to a `String`.
  6. ///
  7. /// This trait is automatically implemented for any type which implements the
  8. /// [`Display`] trait. As such, `ToString` shouldn't be implemented directly:
  9. /// [`Display`] should be implemented instead, and you get the `ToString`
  10. /// implementation for free.
  11. ///
  12. /// [`Display`]: fmt::Display
  13. #[cfg_attr(not(test), rustc_diagnostic_item = "ToString")]
  14. #[stable(feature = "rust1", since = "1.0.0")]
  15. pub trait ToString {
  16. /// Converts the given value to a `String`.
  17. ///
  18. /// # Examples
  19. ///
  20. /// Basic usage:
  21. ///
  22. ///
  1. /// let i = 5;
  2. /// let five = String::from("5");
  3. ///
  4. /// assert_eq!(five, i.to_string());
  5. /// ```
  6. #[rustc_conversion_suggestion]
  7. #[stable(feature = "rust1", since = "1.0.0")]
  8. fn to_string(&self) -> String;

}

/// # Panics /// /// In this implementation, the to_string method panics /// if the Display implementation returns an error. /// This indicates an incorrect Display implementation /// since fmt::Write for String never returns an error itself.

[stable(feature = “rust1”, since = “1.0.0”)]Alex Crichton, 6 years ago: • std: Stabilize the std::fmt module

impl ToString for T { // A common guideline is to not inline generic functions. However, // removing #[inline] from this method causes non-negligible regressions. // See https://github.com/rust-lang/rust/pull/74852, the last attempt // to try to remove it.

  1. #[inline]
  2. default fn to_string(&self) -> String {
  3. use fmt::Write;
  4. let mut buf = String::new();
  5. buf.write_fmt(format_args!("{}", self))
  6. .expect("a Display implementation returned an error unexpectedly");
  7. buf
  8. }

}

  1. 举例
  2. ```rust
  3. fn main() {
  4. let s = 3.to_string();
  5. println!("{}", s);
  6. }