10.3 trait Pt.1trait的定义、约束与实现10.3.1. 什么是traittrait 的意思是特征、特质。trait 用来向 Rust 编译器描述某个类型具有哪些能力以及它可以与其他类型共享哪些行为。trait 以抽象方式定义共享行为。还有 trait bounds约束的概念它可以把泛型类型参数约束为实现了特定行为的类型。换句话说就是要求泛型类型参数实现某些 trait。Rust 中的 trait 与其他语言中的接口interface有些类似但仍有区别。10.3.2. 定义一个trait类型的行为由该类型本身可以调用的方法组成。有时不同类型具有相同的方法这时我们就说这些类型共享相同的行为。trait 提供了一种把方法组合在一起的方式从而定义实现某种目的所必需的行为。- 使用trait关键字定义 trait。trait 中的方法既可以只写方法签名以;结尾、没有方法体也可以提供默认实现后文会讲到- 一个 trait 可以有多个方法每个需要实现方提供的方法签名单独占一行并以;结尾- 对于没有默认实现的方法实现该 trait 的类型必须提供具体的方法体例如pub trait Summary { fn summarize(self) - String; }在trait前面加上pub表示它是公共的。这个 trait 名为Summary其中包含一个名为summarize的方法签名。除了self之外没有其他参数返回类型是String签名以;结束。它没有方法体因此没有具体实现。当然一个trait下可以有很多方法签名pub trait Summary { fn summarize(self) - String; fn summarize1(self) - String; fn summarize2(self) - String; //...... }10.3.3. 在类型上实现trait为类型实现 trait 与为类型实现方法很类似但也有不同之处。为类型实现方法的写法是在impl关键字后面跟上类型impl Yyyy {....}为类型实现 trait 的写法则是impl Xxxx for Yyyy {....}Xxxx指的是 trait 名Yyyy指的是类型名在花括号内需要为 trait 中的方法签名写出具体实现例如lib.rspub trait Summary { fn summarize(self) - String; } pub struct NewsArticle { pub headline: String, pub location: String, pub author: String, pub content: String, } impl Summary for NewsArticle { fn summarize(self) - String { format!({}, by {} ({}), self.headline, self.author, self.location) } } pub struct Tweet { pub username: String, pub content: String, pub reply: bool, pub retweet: bool, } impl Summary for Tweet { fn summarize(self) - String { format!({}: {}, self.username, self.content) } }结构体NewsArticle表示新闻文章。它有四个字段headline表示标题location表示地点author表示作者content表示内容结构体Tweet表示 X前身为 Twitter上的推文。它有四个字段username、content、reply和retweet这两个结构体类型当然不同它们的大多数字段也不同。但它们都可以有相同的行为——提供摘要Summary因此分别在这两个类型上实现了Summary。impl Summary for NewsArticle { fn summarize(self) - String { format!({}, by {} ({}), self.headline, self.author, self.location) } }这段代码为NewsArticle实现了该 trait。因为 trait 定义中包含summarize方法签名所以这里必须写出具体实现使用format!宏把self.headline、self.author和self.location组合成一个字符串并返回。impl Summary for Tweet { fn summarize(self) - String { format!({}: {}, self.username, self.content) } }这段代码也为Tweet实现了该 trait同样提供了summarize的具体实现使用format!宏把self.username和self.content组合成一个字符串并返回。现在来到main.rs看看实例是如何调用的use RustStudy::{Summary, Tweet}; fn main() { let tweet Tweet { username: String::from(horse_ebooks), content: String::from( of course, as you probably already know, people, ), reply: false, retweet: false, }; println!(1 new tweet: {}, tweet.summarize()); }记住我们的代码写在lib.rs中在main.rs中使用之前需要先把它引入作用域。写法是use 你的package名::...::你需要的模块;你的 package 名就是Cargo.toml中的项目名把它复制过来即可。引入Summary是因为使用了Summarytrait 下的summarize方法引入Tweet是因为使用了Tweet结构体。看一下输出1 new tweet: horse_ebooks: of course, as you probably already know, people10.3.4. trait的约束想要为某个类型实现某个 trait前提条件是- 该类型本身例如Tweet或该 trait 本身例如让Vector实现本地的Summary必须在本地 crate 中定义- 不能为外部类型实现外部 trait。例如在本地 crate 中为标准库的Vector实现标准库的Displaytrait这个限制是语言一致性规则的一部分。更具体地说它是孤儿规则之所以这样命名是因为父类型并没有定义在当前 crate 中。这条规则确保其他人的代码不能随意破坏你的代码反之亦然。如果没有这条规则两个 crate 可以为同一个类型实现同一个 traitRust 就不知道该使用哪个实现。10.3.5. 默认实现有时为 trait 中的某些或全部方法提供默认行为非常有用。这让我们不必为每一个类型的实现都提供自定义行为。我们仍然可以为特定类型实现 trait 方法。当为某些类型实现 trait 时我们可以选择保留或覆盖每个方法的默认实现。之前的写法是pub trait Summary { fn summarize(self) - String; }之前的版本只写了方法签名没有提供实现但实际上可以添加默认实现默认实现pub trait Summary { fn summarize(self) - String { String::from((Read more...)) } }这里的默认实现只是返回字符串(Read more...)。因为这个方法在 trait 中已经有默认实现了所以具体类型可以直接使用该默认实现而不必自己再提供实现。以NewsArticle为例它原本有自己的实现也可以叫做对默认实现的覆盖impl Summary for NewsArticle { fn summarize(self) - String { format!({}, by {} ({}), self.headline, self.author, self.location) } }如果删掉这个具体实现NewsArticle就会使用默认实现impl Summary for NewsArticle {}还有一点需要知道带有默认实现的方法可以调用同一trait中的其他方法即使那些方法没有默认实现pub trait Summary { fn summarize_author(self) - String; fn summarize(self) - String { format!((Read more from {}...), self.summarize_author()) } }summarize的默认实现调用了summarize_author即使summarize_author只是一个签名、没有具体实现。但如果想为某个类型实现summarize就需要先实现summarize_authorimpl Summary for NewsArticle { fn summarize_author(self) - String { format!({}, self.author) } }PS由于NewsArticle使用了summarize的默认实现所以这里不需要再写summarize的实现。这种写法有一点需要注意无法从被覆盖的方法实现中调用默认实现。