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());
}

注意item1item2 可以是不同类型(只要都实现了 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 实战:实现一个泛型排序工具

项目需求

  1. 实现泛型排序函数
  2. 支持自定义比较逻辑
  3. 支持多种数据类型

实现代码

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

为自定义类型实现 DisplayPartialEq 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:类型转换

使用 FromInto 实现自定义类型之间的转换。

参考答案

#[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)

本章小结

  1. 泛型:通过类型参数实现代码复用,编译期单态化,无运行时开销
  2. Trait:定义共享行为,类似接口但更强大
  3. Trait Bound:约束泛型参数必须实现特定 Trait
  4. 标准 Trait:Debug、Display、Clone、Copy、PartialEq、From/Into 等
  5. 默认实现和 Supertrait:提供灵活的 Trait 组合方式
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐