trait
约 1587 字大约 5 分钟
2025-08-03
trait 是 Rust 用来描述 "某类行为能力" 的接口,类似其他语言中的 interface
重要
任何类型只要 impl 某个 trait,就能在编译期被当作具备这些方法的类型来使用
定义 trait
一个类型的行为由其可供调用的方法构成。如果不同类型能调用相同的方法,也就意味着它们就共享相同行为
trait 就是把方法签名组合成一个行为契约,实现它的类型必须提供这些方法,从而保证具备这组行为。定义一个 trait 就是使用关键字 trait
定义 trait
pub trait Summary {
// 必须实现
fn summarize(&self) -> String;
// 默认实现,可重写
fn headline(&self) -> String {
String::from("headline")
}
}注
签名里可用 &self、&mut self、Self(实现该 trait 的具体类型)。有默认实现的方法可以不重写,必须实现的方法是契约的一部分
实现 trait
在类型上实现 trait 类似于实现结构体的方法,区别在与 impl 关键字后需要提供实现特征的名称,接着是 for 和需要实现特征的类型的名称
重要
trait不止局限于结构体,还可以为枚举、i32、数组、切片等实现trait可被多个类型实现,同一个类型也可以实现多个trait- 受孤儿规则约束,不能为外部类型实现外部
trait
为结构体实现 trait
pub trait Summary {
fn summarize(&self) -> String;
}
#[allow(dead_code)]
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
)
}
}
#[allow(dead_code)]
pub struct SocialPost {
pub username: String,
pub content: String,
pub reply: bool,
pub repost: bool,
}
impl Summary for SocialPost {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}
fn main() {
let post = SocialPost {
username: String::from("horse_ebooks"),
content: String::from(
"of course, as you probably already know, people",
),
reply: false,
repost: false,
};
println!("1 new post: {}", post.summarize());
}默认实现
默认实现指的是在 trait 中就给方法提供了一个通用的实现,类型在 impl 的时候可以直接服用,也可以选择重写
带有默认实现的 Summary
pub trait Summary {
fn summarize_author(&self) -> String;
fn summarize(&self) -> String {
format!("(Read more from {}...)", self.summarize_author())
}
}
pub struct SocialPost {
pub username: String,
pub content: String,
pub reply: bool,
pub repost: bool,
}
impl Summary for SocialPost {
fn summarize_author(&self) -> String {
format!("@{}", self.username)
}
}
fn main() {
let post = SocialPost {
username: String::from("horse_ebooks"),
content: String::from(
"of course, as you probably already know, people",
),
reply: false,
repost: false,
};
println!("1 new social post: {}", post.summarize());
}类型约束
impl Trait
把形参的类型标注写成 impl Trait 表示参数类型被限定在了实现了该 trait 的类型中。这种方式也被称为 编译期单态化
fn notify(item: &impl Summary) {
println!("Breaking news! {}", item.summarize());
}如果直接将参数的类型标注为 Trait(惯例写成 &dyn Trait),则变成了动态派发的 trait 对象,此时要求该 trait 满足对象安全
fn notify(item: &dyn Summary) {
println!("Breaking news! {}", item.summarize());
}重要
impl Trait:编译期单态化,静态派发,可内联。通常更快,编译期为每种具体类型生成代码;不同impl Trait形参可各自是不同的实现者&dyn Trait:trait 对象,动态派发,同一函数体适配所有实现者,需对象安全,有 vtable 间接开销
返回实现了 trait 的类型
也可在返回值中使用 impl trait 语法,表明返回了实现某个 trait 的类型
pub trait Summary {
fn summarize(&self) -> String;
}
pub struct SocialPost {
pub username: String,
pub content: String,
pub reply: bool,
pub repost: bool,
}
impl Summary for SocialPost {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}
fn returns_summarizable() -> impl Summary {
SocialPost {
username: String::from("horse_ebooks"),
content: String::from(
"of course, as you probably already know, people",
),
reply: false,
repost: false,
}
}注
返回位置的 impl Trait 也是静态派发,整个函数体必须返回同一种具体类型;如果存在不同分支返回不同具体类型,即便都它们都实现了该 trait,但 impl Trait 不是 trait 对象,必须在编译期确定为某一具体类型。因此将会报错
若需要返回多种实现者,可改为 Box<dyn Summary>,或让各分支构造同一种包装类型
特征约束
impl Trait 的形式虽然更直观,但是其本质上特征约束语法 的语法糖
注意
并不是说 &T 就是表示它是 &trait,而是等价于 &impl trait
fn notify<T: Summary>(item: &T) {
println!("Breaking news! {}", item.summarize());
}特征约束的形式更适用于更复杂的场景,impl trait 更适用于短小的场景
pub fn notify<T: Summary>(item1: &T, item2: &T) {
println!("Breaking news! {}", item.summarize());
}pub fn notify(item1: &impl Summary, item2: &impl Summary) {
println!("Breaking news! {}", item.summarize());
}指定多个特征约束
可通过 + 指定多个特征约束
fn notify<T: Summary + Display> (item: &T) {
println!("Breaking news! {}", item.summarize());
}where 语句
当泛型参数过多,且每个都有多个 trait 约束时,把约束都挤在类型参数列表里会让函数签名又长又难读。因此可以把约束部分移动到函数签名后的 where 语句中
fn some_function<T, U>(t: &T, u: &U) -> i32
where
T: Display + Clone,
U: Clone + Debug,
{
let t_len = format!("{t}").len() as i32;
let u_len = format!("{u:?}").len() as i32;
t_len + u_len
}注
where 可用在所有带泛型参数/约束的位置
有条件的实现方法
use std::fmt::Display;
struct Pair<T> {
x: T,
y: T,
}
impl<T> Pair<T> {
fn new(x: T, y: T) -> Self {
Self { x, y }
}
}
impl<T> Pair<T>
where
T: PartialOrd + Display,
{
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);
}
}
}