第6章 泛型与 Trait
·
6.1 泛型函数与泛型结构体
6.1.1 为什么需要泛型?
问题:重复的代码
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 + Copy>(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);
}
6.1.2 泛型函数语法
fn function_name<T>(parameter: T) -> T {
// 函数体
}
多个泛型参数:
fn print_pair<T: std::fmt::Debug, U: std::fmt::Debug>(a: T, b: U) {
println!("a = {:?}, b = {:?}", a, b);
}
fn main() {
print_pair(5, "hello");
print_pair(3.14, true);
}
6.1.3 泛型结构体
struct Point<T> {
x: T,
y: T,
}
fn main() {
let integer_point = Point { x: 5, y: 10 };
let float_point = Point { x: 1.0, y: 4.0 };
println!("整数点:({}, {})", integer_point.x, integer_point.y);
println!("浮点点:({}, {})", float_point.x, float_point.y);
}
多个泛型参数:
struct Point<T, U> {
x: T,
y: U,
}
fn main() {
let point = Point { x: 5, y: 4.0 };
println!("x = {}, y = {}", point.x, point.y);
}
6.2 泛型枚举与泛型方法
6.2.1 泛型枚举
我们已经见过的泛型枚举:
enum Option<T> {
Some(T),
None,
}
enum Result<T, E> {
Ok(T),
Err(E),
}
自定义泛型枚举:
enum Either<L, R> {
Left(L),
Right(R),
}
fn main() {
let left: Either<i32, String> = Either::Left(42);
let right: Either<i32, String> = Either::Right(String::from("hello"));
match left {
Either::Left(n) => println!("左:{}", n),
Either::Right(s) => println!("右:{}", s),
}
}
6.2.2 泛型方法
struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
fn main() {
let p = Point { x: 5, y: 10 };
println!("p.x = {}", p.x());
}
为特定类型实现方法:
impl Point<f32> {
fn distance_from_origin(&self) -> f32 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}
fn main() {
let p = Point { x: 3.0, y: 4.0 };
println!("距离原点:{}", p.distance_from_origin());
// let p2 = Point { x: 3, y: 4 };
// p2.distance_from_origin(); // ❌ 错误!只对 f32 实现
}
混合泛型参数:
struct Point<T, U> {
x: T,
y: U,
}
impl<T, U> Point<T, U> {
fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
Point {
x: self.x,
y: other.y,
}
}
}
fn main() {
let p1 = Point { x: 5, y: 10.4 };
let p2 = Point { x: "Hello", y: 'c' };
let p3 = p1.mixup(p2);
println!("p3.x = {}, p3.y = {}", p3.x, p3.y); // 5, c
}
6.3 Trait 定义与实现
Trait 类似于其他语言的接口(interface),定义共享的行为。
6.3.1 定义 Trait
trait Summary {
fn summarize(&self) -> String;
}
6.3.2 为类型实现 Trait
struct NewsArticle {
headline: String,
location: String,
author: String,
content: String,
}
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!("{}, by {} ({})", self.headline, self.author, self.location)
}
}
struct Tweet {
username: String,
content: String,
reply: bool,
retweet: bool,
}
impl Summary for Tweet {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}
fn main() {
let article = NewsArticle {
headline: String::from("Rust 1.70 发布"),
location: String::from("互联网"),
author: String::from("Rust 团队"),
content: String::from("Rust 1.70 带来了许多新特性..."),
};
println!("新闻:{}", article.summarize());
let tweet = Tweet {
username: String::from("rust_lang"),
content: String::from("Rust 很棒!"),
reply: false,
retweet: false,
};
println!("推文:{}", tweet.summarize());
}
6.4 Trait 作为参数(impl Trait)
6.4.1 impl Trait 语法
fn notify(item: &impl Summary) {
println!("突发新闻!{}", item.summarize());
}
fn main() {
let article = NewsArticle {
headline: String::from("重要新闻"),
location: String::from("北京"),
author: String::from("记者"),
content: String::from("内容..."),
};
notify(&article);
}
6.4.2 多个参数
fn notify(item1: &impl Summary, item2: &impl Summary) {
println!("item1: {}", item1.summarize());
println!("item2: {}", item2.summarize());
}
注意:item1 和 item2 可以是不同类型(只要都实现了 Summary)。
6.4.3 返回实现 Trait 的类型
fn returns_summarizable() -> impl Summary {
Tweet {
username: String::from("rust_lang"),
content: String::from("Rust 很棒!"),
reply: false,
retweet: false,
}
}
限制:只能返回单一类型
fn returns_summarizable(switch: bool) -> impl Summary {
if switch {
NewsArticle { /* ... */ } // ❌ 错误!
} else {
Tweet { /* ... */ }
}
}
6.5 Trait Bound 语法与多重约束
6.5.1 Trait Bound 语法
impl Trait 是 Trait Bound 的语法糖。
// impl Trait 语法
fn notify(item: &impl Summary) {
println!("{}", item.summarize());
}
// Trait Bound 语法(等价)
fn notify<T: Summary>(item: &T) {
println!("{}", item.summarize());
}
6.5.2 多个 Trait Bound
use std::fmt::Display;
fn notify<T: Summary + Display>(item: &T) {
println!("{}", item);
println!("{}", item.summarize());
}
6.5.3 where 子句
当 Trait Bound 很多时,使用 where 子句更清晰。
// 不使用 where
fn some_function<T: Display + Clone, U: Clone + Debug>(t: &T, u: &U) -> i32 {
// ...
}
// 使用 where(更清晰)
fn some_function<T, U>(t: &T, u: &U) -> i32
where
T: Display + Clone,
U: Clone + Debug,
{
// ...
}
6.5.4 有条件地实现方法
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 }
}
}
// 只为实现了 Display + PartialOrd 的类型实现 cmp_display
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();
}
6.6 常用标准 Trait(Display、Debug、Clone、Copy、PartialEq、PartialOrd、From/Into)
6.6.1 Debug
用于调试输出,使用 {:?} 或 {:#?}。
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 5, y: 10 };
println!("{:?}", p); // Point { x: 5, y: 10 }
println!("{:#?}", p); // 美化输出
}
6.6.2 Display
用于用户友好的输出,使用 {}。
use std::fmt;
struct Point {
x: i32,
y: i32,
}
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
fn main() {
let p = Point { x: 5, y: 10 };
println!("{}", p); // (5, 10)
}
6.6.3 Clone
显式复制值。
#[derive(Clone)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p1 = Point { x: 5, y: 10 };
let p2 = p1.clone();
println!("p1: ({}, {})", p1.x, p1.y);
println!("p2: ({}, {})", p2.x, p2.y);
}
6.6.4 Copy
隐式复制值(栈上的简单类型)。
#[derive(Copy, Clone)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p1 = Point { x: 5, y: 10 };
let p2 = p1; // 复制,不是移动
println!("p1: ({}, {})", p1.x, p1.y); // p1 仍然可用
println!("p2: ({}, {})", p2.x, p2.y);
}
注意:实现 Copy 必须同时实现 Clone。
6.6.5 PartialEq 和 Eq
比较相等性。
#[derive(PartialEq)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p1 = Point { x: 5, y: 10 };
let p2 = Point { x: 5, y: 10 };
let p3 = Point { x: 3, y: 7 };
println!("p1 == p2: {}", p1 == p2); // true
println!("p1 == p3: {}", p1 == p3); // false
}
6.6.6 PartialOrd 和 Ord
比较大小。
#[derive(PartialEq, PartialOrd)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p1 = Point { x: 5, y: 10 };
let p2 = Point { x: 3, y: 7 };
println!("p1 > p2: {}", p1 > p2); // true
}
6.6.7 From 和 Into
类型转换。
struct Number {
value: i32,
}
impl From<i32> for Number {
fn from(item: i32) -> Self {
Number { value: item }
}
}
fn main() {
let num = Number::from(30);
println!("Number: {}", num.value);
// Into 是 From 的反向,自动实现
let num: Number = 30.into();
println!("Number: {}", num.value);
}
6.7 Trait 默认实现与继承(Supertrait)
6.7.1 默认实现
trait Summary {
fn summarize_author(&self) -> String;
fn summarize(&self) -> String {
format!("(阅读更多来自 {}...)", self.summarize_author())
}
}
struct Tweet {
username: String,
content: String,
}
impl Summary for Tweet {
fn summarize_author(&self) -> String {
format!("@{}", self.username)
}
// summarize 使用默认实现
}
fn main() {
let tweet = Tweet {
username: String::from("rust_lang"),
content: String::from("Rust 很棒!"),
};
println!("{}", tweet.summarize());
}
6.7.2 Supertrait
要求实现某个 Trait 的类型也必须实现另一个 Trait。
use std::fmt;
trait OutlinePrint: fmt::Display {
fn outline_print(&self) {
let output = self.to_string();
let len = output.len();
println!("{}", "*".repeat(len + 4));
println!("*{}*", " ".repeat(len + 2));
println!("* {} *", output);
println!("*{}*", " ".repeat(len + 2));
println!("{}", "*".repeat(len + 4));
}
}
struct Point {
x: i32,
y: i32,
}
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
impl OutlinePrint for Point {}
fn main() {
let p = Point { x: 5, y: 10 };
p.outline_print();
}
6.8 实战:实现一个泛型排序工具
项目需求:
- 实现泛型排序函数
- 支持自定义比较逻辑
- 支持多种数据类型
实现代码:
use std::fmt::Display;
// 泛型排序函数(冒泡排序)
fn bubble_sort<T: PartialOrd>(arr: &mut [T]) {
let len = arr.len();
for i in 0..len {
for j in 0..len - 1 - i {
if arr[j] > arr[j + 1] {
arr.swap(j, j + 1);
}
}
}
}
// 使用自定义比较器排序
fn bubble_sort_by<T, F>(arr: &mut [T], mut compare: F)
where
F: FnMut(&T, &T) -> std::cmp::Ordering,
{
let len = arr.len();
for i in 0..len {
for j in 0..len - 1 - i {
if compare(&arr[j], &arr[j + 1]) == std::cmp::Ordering::Greater {
arr.swap(j, j + 1);
}
}
}
}
// 打印数组
fn print_array<T: Display>(arr: &[T], name: &str) {
print!("{}: [", name);
for (i, item) in arr.iter().enumerate() {
if i > 0 {
print!(", ");
}
print!("{}", item);
}
println!("]");
}
// 自定义类型
#[derive(Debug, PartialEq, PartialOrd)]
struct Person {
name: String,
age: u32,
}
impl Display for Person {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}({}岁)", self.name, self.age)
}
}
fn main() {
println!("=== 泛型排序工具 ===\n");
// 排序整数
let mut numbers = vec![64, 34, 25, 12, 22, 11, 90];
print_array(&numbers, "排序前");
bubble_sort(&mut numbers);
print_array(&numbers, "排序后");
println!();
// 排序浮点数
let mut floats = vec![3.14, 2.71, 1.41, 1.73];
print_array(&floats, "排序前");
bubble_sort(&mut floats);
print_array(&floats, "排序后");
println!();
// 排序字符串
let mut strings = vec!["banana", "apple", "cherry", "date"];
print_array(&strings, "排序前");
bubble_sort(&mut strings);
print_array(&strings, "排序后");
println!();
// 排序自定义类型
let mut people = vec![
Person { name: String::from("Alice"), age: 30 },
Person { name: String::from("Bob"), age: 25 },
Person { name: String::from("Charlie"), age: 35 },
];
print_array(&people, "按默认排序前");
bubble_sort(&mut people);
print_array(&people, "按默认排序后");
println!();
// 按年龄降序排序
bubble_sort_by(&mut people, |a, b| b.age.cmp(&a.age));
print_array(&people, "按年龄降序");
}
常见误区与陷阱
误区 1:忘记 Trait Bound
// ❌ 错误
fn largest<T>(list: &[T]) -> T {
let mut largest = list[0];
for &item in list {
if item > largest { // 错误!T 没有实现 PartialOrd
largest = item;
}
}
largest
}
// ✅ 正确
fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
let mut largest = list[0];
for &item in list {
if item > largest {
largest = item;
}
}
largest
}
误区 2:混淆 Clone 和 Copy
// Copy:隐式复制,只能用于栈上的简单类型
#[derive(Copy, Clone)]
struct Point {
x: i32,
y: i32,
}
// Clone:显式复制,可以用于任何类型
#[derive(Clone)]
struct Person {
name: String, // String 不是 Copy
}
误区 3:孤儿规则
不能为外部类型实现外部 Trait。
// ❌ 错误:Vec 和 Display 都是外部的
impl Display for Vec<i32> {
// ...
}
// ✅ 正确:创建新类型包装
struct MyVec(Vec<i32>);
impl Display for MyVec {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.0)
}
}
实战练习
练习 6.1:泛型栈
实现一个泛型栈 Stack<T>,支持 push、pop、peek 操作。
参考答案:
struct Stack<T> {
elements: Vec<T>,
}
impl<T> Stack<T> {
fn new() -> Stack<T> {
Stack {
elements: Vec::new(),
}
}
fn push(&mut self, item: T) {
self.elements.push(item);
}
fn pop(&mut self) -> Option<T> {
self.elements.pop()
}
fn peek(&self) -> Option<&T> {
self.elements.last()
}
fn is_empty(&self) -> bool {
self.elements.is_empty()
}
fn size(&self) -> usize {
self.elements.len()
}
}
fn main() {
let mut stack: Stack<i32> = Stack::new();
stack.push(1);
stack.push(2);
stack.push(3);
println!("栈大小:{}", stack.size());
println!("栈顶元素:{:?}", stack.peek());
while let Some(top) = stack.pop() {
println!("弹出:{}", top);
}
println!("栈是否为空:{}", stack.is_empty());
// 泛型:也可以存储字符串
let mut string_stack: Stack<String> = Stack::new();
string_stack.push(String::from("hello"));
string_stack.push(String::from("world"));
println!("字符串栈顶:{:?}", string_stack.peek());
}
输出:
栈大小:3
栈顶元素:Some(3)
弹出:3
弹出:2
弹出:1
栈是否为空:true
字符串栈顶:Some("world")
练习 6.2:实现 Trait
为自定义类型实现 Display 和 PartialEq Trait。
参考答案:
use std::fmt;
#[derive(Debug)]
struct Color {
red: u8,
green: u8,
blue: u8,
}
impl fmt::Display for Color {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "#{:02X}{:02X}{:02X}", self.red, self.green, self.blue)
}
}
impl PartialEq for Color {
fn eq(&self, other: &Self) -> bool {
self.red == other.red && self.green == other.green && self.blue == other.blue
}
}
fn main() {
let red = Color { red: 255, green: 0, blue: 0 };
let green = Color { red: 0, green: 255, blue: 0 };
let also_red = Color { red: 255, green: 0, blue: 0 };
println!("红色:{}", red);
println!("绿色:{}", green);
println!("red == also_red: {}", red == also_red);
println!("red == green: {}", red == green);
}
输出:
红色:#FF0000
绿色:#00FF00
red == also_red: true
red == green: false
练习 6.3:Trait Bound
编写函数,接受实现了 Display + Clone 的参数。
参考答案:
use std::fmt::Display;
fn print_and_clone<T: Display + Clone>(item: &T) -> T {
println!("原始值:{}", item);
let cloned = item.clone();
println!("克隆值:{}", cloned);
cloned
}
fn main() {
let s = String::from("hello");
let cloned_s = print_and_clone(&s);
println!("返回值:{}", cloned_s);
println!("原始值仍可用:{}", s);
println!();
let n = 42;
let cloned_n = print_and_clone(&n);
println!("返回值:{}", cloned_n);
}
输出:
原始值:hello
克隆值:hello
返回值:hello
原始值仍可用:hello
原始值:42
克隆值:42
返回值:42
练习 6.4:类型转换
使用 From 和 Into 实现自定义类型之间的转换。
参考答案:
#[derive(Debug)]
struct Celsius(f64);
#[derive(Debug)]
struct Fahrenheit(f64);
impl From<Celsius> for Fahrenheit {
fn from(c: Celsius) -> Self {
Fahrenheit(c.0 * 9.0 / 5.0 + 32.0)
}
}
impl From<Fahrenheit> for Celsius {
fn from(f: Fahrenheit) -> Self {
Celsius((f.0 - 32.0) * 5.0 / 9.0)
}
}
fn main() {
let boiling = Celsius(100.0);
let boiling_f: Fahrenheit = boiling.into();
println!("100°C = {:?}", boiling_f);
let body_temp = Fahrenheit(98.6);
let body_temp_c = Celsius::from(body_temp);
println!("98.6°F = {:?}", body_temp_c);
let freezing = Celsius(0.0);
let freezing_f = Fahrenheit::from(freezing);
println!("0°C = {:?}", freezing_f);
}
输出:
100°C = Fahrenheit(212.0)
98.6°F = Celsius(37.0)
0°C = Fahrenheit(32.0)
本章小结
- 泛型:通过类型参数实现代码复用,编译期单态化,无运行时开销
- Trait:定义共享行为,类似接口但更强大
- Trait Bound:约束泛型参数必须实现特定 Trait
- 标准 Trait:Debug、Display、Clone、Copy、PartialEq、From/Into 等
- 默认实现和 Supertrait:提供灵活的 Trait 组合方式
更多推荐
所有评论(0)