泛型和 Trait
泛型是编写可复用代码的核心特性,Trait 定义共享行为。两者结合可以实现灵活且类型安全的抽象。
泛型数据类型
函数中的泛型
使用泛型编写适用于多种类型的函数:
// 不使用泛型:需要为每种类型写一个函数
fn largest_i32(list: &[i32]) -> &i32 {
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
}
fn largest_char(list: &[char]) -> &char {
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
}
// 使用泛型:一个函数适用于多种类型
fn largest<T: PartialOrd>(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
}
fn main() {
let number_list = vec![34, 50, 25, 100, 65];
let result = largest(&number_list);
println!("最大数字: {}", result);
let char_list = vec!['y', 'm', 'a', 'q'];
let result = largest(&char_list);
println!("最大字符: {}", result);
}
解释:
<T>声明类型参数T: PartialOrd是 trait bound,限制 T 必须可比较- 泛型不会降低运行时性能(单态化)
结构体中的泛型
// 单个类型参数
struct Point<T> {
x: T,
y: T,
}
fn main() {
let integer = Point { x: 5, y: 10 };
let float = Point { x: 1.0, y: 4.0 };
// 错误:x 和 y 必须是相同类型
// let mixed = Point { x: 5, y: 4.0 };
}
// 多个类型参数
struct PointMulti<T, U> {
x: T,
y: U,
}
fn main() {
let p1 = PointMulti { x: 5, y: 10 };
let p2 = PointMulti { x: 1.0, y: "hello" };
let p3 = PointMulti { x: "left", y: 'c' };
}
枚举中的泛型
// 标准库中的 Option 和 Result
enum Option<T> {
Some(T),
None,
}
enum Result<T, E> {
Ok(T),
Err(E),
}
// 自定义泛型枚举
enum Container<T> {
Single(T),
Pair(T, T),
Triple(T, T, T),
}
fn main() {
let single = Container::Single(42);
let pair = Container::Pair(1, 2);
let triple = Container::Triple('a', 'b', 'c');
}
方法定义中的泛型
struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
// 泛型方法
fn x(&self) -> &T {
&self.x
}
fn y(&self) -> &T {
&self.y
}
}
// 为特定类型实现方法
impl Point<f32> {
fn distance_from_origin(&self) -> f32 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}
// 混合泛型
impl<T, U> PointMulti<T, U> {
fn mixup<V, W>(self, other: PointMulti<V, W>) -> PointMulti<T, W> {
PointMulti {
x: self.x,
y: other.y,
}
}
}
fn main() {
let p = Point { x: 5, y: 10 };
println!("x = {}", p.x());
let p_float = Point { x: 1.0, y: 2.0 };
println!("距离原点: {}", p_float.distance_from_origin());
// 整数 Point 没有这个方法
// p.distance_from_origin(); // 错误!
}
泛型默认类型
// 为泛型参数指定默认类型
struct Point<T = i32> {
x: T,
y: T,
}
fn main() {
// 使用默认类型
let p1 = Point { x: 1, y: 2 };
// 指定其他类型
let p2: Point<f64> = Point { x: 1.0, y: 2.0 };
}
Trait 定义
定义和实现 Trait
// 定义 Trait
pub trait Summary {
fn summarize(&self) -> String;
}
// 为类型实现 Trait
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 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 article = NewsArticle {
headline: String::from("重大新闻"),
location: String::from("北京"),
author: String::from("记者张三"),
content: String::from("今日发生了一件大事..."),
};
println!("{}", article.summarize());
let post = SocialPost {
username: String::from("user123"),
content: String::from("今天天气真好"),
reply: false,
repost: false,
};
println!("{}", post.summarize());
}
默认实现
pub trait Summary {
// 带默认实现的方法
fn summarize(&self) -> String {
String::from("(阅读更多...)")
}
// 可以调用其他方法
fn summarize_author(&self) -> String;
fn summarize_with_author(&self) -> String {
format!("({}...)", self.summarize_author())
}
}
impl Summary for SocialPost {
fn summarize_author(&self) -> String {
format!("@{}", self.username)
}
// 可以覆盖默认实现
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}
// 使用默认实现
impl Summary for NewsArticle {
fn summarize_author(&self) -> String {
self.author.clone()
}
// summarize 使用默认实现
}
fn main() {
let post = SocialPost {
username: String::from("user123"),
content: String::from("你好世界"),
reply: false,
repost: false,
};
println!("{}", post.summarize());
println!("{}", post.summarize_with_author());
}
Trait 作为参数
pub trait Summary {
fn summarize(&self) -> String;
}
// impl Trait 语法
pub fn notify(item: &impl Summary) {
println!("突发新闻! {}", item.summarize());
}
// Trait Bound 语法
pub fn notify2<T: Summary>(item: &T) {
println!("突发新闻! {}", item.summarize());
}
fn main() {
let article = NewsArticle { /* ... */ };
notify(&article);
notify2(&article);
}
多个 Trait
use std::fmt::Display;
// 多个 Trait Bound
pub fn notify(item: &(impl Summary + Display)) {
println!("摘要: {}", item.summarize());
println!("显示: {}", item);
}
// 使用泛型参数
pub fn notify2<T: Summary + Display>(item: &T) {
println!("摘要: {}", item.summarize());
println!("显示: {}", item);
}
// where 子句(更清晰)
pub fn some_function<T, U>(t: &T, u: &U) -> String
where
T: Display + Clone,
U: Clone + Summary,
{
format!("{} - {}", t, u.summarize())
}
返回 Trait
pub trait Summary {
fn summarize(&self) -> String;
}
// 返回实现了 Trait 的类型
fn returns_summarizable() -> impl Summary {
SocialPost {
username: String::from("user"),
content: String::from("内容"),
reply: false,
repost: false,
}
}
fn main() {
let post = returns_summarizable();
println!("{}", post.summarize());
}
限制:只能返回单一类型:
// 错误:不能返回不同类型
fn returns_summarizable(switch: bool) -> impl Summary {
if switch {
NewsArticle { /* ... */ } // 类型 A
} else {
SocialPost { /* ... */ } // 类型 B
}
}
条件实现
use std::fmt::Display;
struct Pair<T> {
x: T,
y: T,
}
// 为所有 Pair<T> 实现 new
impl<T> Pair<T> {
fn new(x: T, y: T) -> Self {
Self { x, y }
}
}
// 条件实现:只有当 T 实现了 Display 和 PartialOrd 时
impl<T: Display + PartialOrd> Pair<T> {
fn cmp_display(&self) {
if self.x >= self.y {
println!("最大的是 x = {}", self.x);
} else {
println!("最大的是 y = {}", self.y);
}
}
}
fn main() {
let pair = Pair::new(10, 20);
pair.cmp_display(); // i32 实现了 Display 和 PartialOrd
}
孤儿规则
实现 trait 时必须满足:trait 或类型至少有一个是本地定义的。
// 在本地 crate 中为外部类型实现本地 trait
// ✅ 允许
impl Summary for Vec<String> {
fn summarize(&self) -> String {
self.join(", ")
}
}
// 在本地 crate 中为本地类型实现外部 trait
// ✅ 允许
impl Display for SocialPost {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.username)
}
}
// 在本地 crate 中为外部类型实现外部 trait
// ❌ 禁止
// impl Display for Vec<String> { ... }
常用标准 Trait
Clone 和 Copy
#[derive(Clone, Copy)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p1 = Point { x: 1, y: 2 };
let p2 = p1; // Copy:p1 仍然有效
println!("p1: ({}, {})", p1.x, p1.y);
println!("p2: ({}, {})", p2.x, p2.y);
}
Debug
#[derive(Debug)]
struct Person {
name: String,
age: u32,
}
fn main() {
let person = Person {
name: String::from("张三"),
age: 30,
};
println!("{:?}", person); // 调试格式
println!("{:#?}", person); // 美化调试格式
}
Default
#[derive(Default)]
struct Config {
timeout: u32,
retries: u32,
debug: bool,
}
fn main() {
// 使用默认值
let config = Config::default();
println!("超时: {}, 重试: {}, 调试: {}",
config.timeout, config.retries, config.debug);
// 部分更新
let config = Config {
timeout: 60,
..Default::default()
};
}
From 和 Into
// From trait
impl From<i32> for MyNumber {
fn from(value: i32) -> Self {
MyNumber(value)
}
}
// Into trait(自动实现)
let num: MyNumber = 42.into();
// 实际使用
let s = String::from("hello");
let n: i32 = "42".parse().unwrap();
Deref 和 DerefMut
use std::ops::Deref;
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
}
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
fn main() {
let x = MyBox::new(5);
println!("{}", *x); // 解引用
}
Drop
struct CustomSmartPointer {
data: String,
}
impl Drop for CustomSmartPointer {
fn drop(&mut self) {
println!("释放 CustomSmartPointer,数据: {}", self.data);
}
}
fn main() {
let c = CustomSmartPointer {
data: String::from("我的数据"),
};
// c 离开作用域时自动调用 drop
println!("CustomSmartPointer 创建完毕");
}
// 输出:
// CustomSmartPointer 创建完毕
// 释放 CustomSmartPointer,数据: 我的数据
Trait 对象
动态分发
pub trait Draw {
fn draw(&self);
}
pub struct Screen {
pub components: Vec<Box<dyn Draw>>,
}
impl Screen {
pub fn run(&self) {
for component in self.components.iter() {
component.draw();
}
}
}
struct Button {
width: u32,
height: u32,
label: String,
}
impl Draw for Button {
fn draw(&self) {
println!("绘制按钮: {} ({}x{})", self.label, self.width, self.height);
}
}
struct TextField {
width: u32,
height: u32,
placeholder: String,
}
impl Draw for TextField {
fn draw(&self) {
println!("绘制文本框: {} ({}x{})", self.placeholder, self.width, self.height);
}
}
fn main() {
let screen = Screen {
components: vec![
Box::new(Button {
width: 50,
height: 10,
label: String::from("确定"),
}),
Box::new(TextField {
width: 100,
height: 20,
placeholder: String::from("输入内容"),
}),
],
};
screen.run();
}
Trait 对象 vs 泛型
| 特性 | 泛型(静态分发) | Trait 对象(动态分发) |
|---|---|---|
| 性能 | 编译时确定,更优 | 运行时查表,略差 |
| 二进制大小 | 每种类型生成一份代码 | 只有一份代码 |
| 灵活性 | 编译时确定类型 | 运行时可混合类型 |
| 对象大小 | 编译时已知 | 需要指针间接访问 |
小结
本章我们学习了:
- 泛型:函数、结构体、枚举、方法中的泛型
- Trait:定义、实现、默认实现、作为参数
- Trait Bound:约束泛型类型的行为
- 标准 Trait:Clone、Copy、Debug、Default、From/Into、Deref、Drop
- Trait 对象:动态分发和运行时多态
练习
- 实现一个泛型函数,查找切片中的最小值和最大值
- 定义一个
Describetrait,为多种类型实现它 - 使用
where子句重写一个复杂的 trait bound 函数 - 实现一个可以存储不同类型组件的
Screen结构