【Rust自学】10.3. trait Pt.1:trait的定义、约束与实现
2026/7/23 18:50:48 网站建设 项目流程

10.3 trait Pt.1:trait的定义、约束与实现

10.3.1. 什么是trait

trait 的意思是特征、特质。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.rs):

pub 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)上的推文。它有四个字段:usernamecontentreplyretweet

这两个结构体类型当然不同,它们的大多数字段也不同。但它们都可以有相同的行为——提供摘要Summary,因此分别在这两个类型上实现了Summary

impl Summary for NewsArticle { fn summarize(&self) -> String { format!("{}, by {} ({})", self.headline, self.author, self.location) } }

这段代码为NewsArticle实现了该 trait。因为 trait 定义中包含summarize方法签名,所以这里必须写出具体实现:使用format!宏把self.headlineself.authorself.location组合成一个字符串并返回。

impl Summary for Tweet { fn summarize(&self) -> String { format!("{}: {}", self.username, self.content) } }

这段代码也为Tweet实现了该 trait,同样提供了summarize的具体实现:使用format!宏把self.usernameself.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, people

10.3.4. trait的约束

想要为某个类型实现某个 trait,前提条件是:
- 该类型本身(例如Tweet)或该 trait 本身(例如让Vector实现本地的Summary)必须在本地 crate 中定义
- 不能为外部类型实现外部 trait。例如,在本地 crate 中为标准库的Vector实现标准库的Displaytrait
这个限制是语言一致性规则的一部分。更具体地说,它是孤儿规则,之所以这样命名,是因为父类型并没有定义在当前 crate 中。这条规则确保其他人的代码不能随意破坏你的代码,反之亦然。如果没有这条规则,两个 crate 可以为同一个类型实现同一个 trait,Rust 就不知道该使用哪个实现。

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_author

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

PS:由于NewsArticle使用了summarize的默认实现,所以这里不需要再写summarize的实现。

这种写法有一点需要注意:无法从被覆盖的方法实现中调用默认实现。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询