01. Trait 定义和实现
Trait 定义共享行为,类似其他语言的接口。用 trait 关键字定义方法签名,用 impl Xxx for YourType 为你的类型实现 trait。可以在 trait 里提供默认实现,实现者可以复用或覆盖。孤儿规则:不能为外部类型实现外部 trait,防止冲突。
rust
trait Summary {
fn summarize(&self) -> String;
// 默认实现
fn summarize_default(&self) -> String {
String::from("(摘要暂无)")
}
}
struct Article {
title: String,
content: String,
}
impl Summary for Article {
fn summarize(&self) -> String {
format!("{} - {}", self.title, &self.content[..20])
}
}
let article = Article {
title: "Rust 入门".into(),
content: "这是一篇关于 Rust...".into(),
};
println!("{}", article.summarize());Rust 的 trait 类似 Go 的 interface 但需要显式实现(impl Trait for Type),编译器帮你检查。
02. Trait 作为参数
函数参数可以用 impl Trait 语法接受实现了指定 trait 的类型,这是语法糖,底层还是泛型。Trait Bound 用 T: Trait 约束泛型参数。+ 号组合多个 trait 条件(T: Summary + Display)。where 子句在复杂约束时让代码更可读。
rust
// impl Trait 语法
fn notify(item: &impl Summary) {
println!("通知: {}", item.summarize());
}
// 等价泛型写法
fn notify_generic<T: Summary>(item: &T) {
println!("通知: {}", item.summarize());
}
// 多个 trait 约束
fn display_and_summarize<T: Summary + std::fmt::Display>(item: &T) {
println!("显示: {} 摘要: {}", item, item.summarize());
}
// where 子句(复杂情况更清晰)
fn complex<T, U>(t: &T, u: &U) -> String
where
T: Summary + Clone,
U: Clone + std::fmt::Debug,
{
format!("{} {:?}", t.summarize(), u)
}函数签名简单用 impl Trait,多个参数各需不同 trait 用泛型 + where。
03. 返回实现了 Trait 的类型
可以用 impl Trait 作为返回类型,隐藏具体类型,适合闭包和迭代器这种类型名很复杂的情况。但它只能返回单一类型,不能运行时动态选择。要运行时多态需要用 trait object:Box<dyn Trait> 或 &dyn Trait,用动态分派在运行时确定调用哪个方法。
rust
fn produce_summarizable() -> impl Summary {
Article {
title: "Rust".into(),
content: "确实好".into(),
}
}
// 返回不同类型?不行!
// fn returns_result(flag: bool) -> impl Summary {
// if flag { Article {..} } else { Tweet {..} }
// }
// 运行时多态用 trait object
fn returns_dyn(flag: bool) -> Box<dyn Summary> {
if flag {
Box::new(Article { title: "A".into(), content: "...".into() })
} else {
Box::new(Tweet { username: "user".into(), content: "...".into() })
}
}
// 使用 trait object
let summaries: Vec<Box<dyn Summary>> = vec![
Box::new(Article { title: "...".into(), content: "...".into() }),
Box::new(Tweet { username: "...".into(), content: "...".into() }),
];impl Trait 返回和 dyn Trait 是不同的:前者编译时单态化,后者运行时动态分派有额外开销。
04. 常用的标准库 Trait
标准库有很多重要的 trait。Display 控制 {} 格式化(给用户看),Debug 控制 {:?}(给开发者看)。Clone 深拷贝,Copy 浅拷贝(栈上复制)。PartialEq 实现 == 比较,PartialOrd 实现排序。Drop 实现析构函数,在值离开作用域时调用,做资源清理。
rust
#[derive(Debug, Clone, PartialEq, PartialOrd)]
struct Point {
x: f64,
y: f64,
}
// 手动实现 Display
impl std::fmt::Display for Point {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
// 手动实现 Drop
struct Database {
conn: Connection,
}
impl Drop for Database {
fn drop(&mut self) {
println!("关闭数据库连接");
// self.conn.close();
}
}
let p = Point { x: 3.0, y: 4.0 };
println!("{}", p); // Display: (3, 4)
println!("{:?}", p); // Debug: Point { x: 3.0, y: 4.0 }Copy 和 Clone 的区别:Copy 是栈上按位复制(简单的值类型),Clone 可以包含堆内存的深拷贝。
05. 关联类型和 supertrait
Trait 里可以定义关联类型(associated type),用 type 关键字。实现时指定具体类型。关联类型比泛型参数更简洁,因为不需要在每处使用都写类型参数。一个 trait 可以要求另一个 trait 作为前提,这叫 supertrait:trait X: Y {},实现 X 必须先实现 Y。
rust
// 关联类型
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
struct Counter {
count: u32,
}
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
self.count += 1;
Some(self.count)
}
}
// supertrait
use std::fmt::Display;
trait Printable: Display {
fn print(&self) {
println!("{}", self);
}
}
// 实现 Printable 必须先实现 Display
impl Printable for Point {}
// 使用
let p = Point { x: 1.0, y: 2.0 };
p.print();什么时候用关联类型什么时候用泛型参数?一个类型只应有一次实现就用关联类型(如 Iterator),多种实现用泛型参数。
知识测验
第 1/4 题正确 0
Rust 中 trait 相当于其他语言的什么?