Tailwind CSSTailwind CSS
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • 搜索未来:SEO与GEO双引擎实战手册
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • Rust 开发入门
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • 搜索未来:SEO与GEO双引擎实战手册
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • Rust 开发入门
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain

8.2 特性与泛型

泛型(Generics)和特性(Traits)是 Rust 语言中最强大的两个特性,它们共同构成了实现代码复用和抽象的基础。简单来说,泛型允许你编写可以处理多种类型的代码,而特性则定义了一组可以被不同类型共享的行为。理解并掌握这两者,是编写高质量、可维护的 Rust 代码的关键。

泛型:类型参数化

泛型是一种将类型作为参数的技术。通过使用泛型,你可以编写一个函数、结构体、枚举或方法,使其能够处理多种不同的具体类型,而无需为每种类型都编写重复的代码。

1. 函数中的泛型

假设我们需要一个函数,它能找出一个数组中最大的元素。如果不用泛型,我们需要为 i32、f64、char 等类型分别编写函数。使用泛型,我们可以编写一个通用的函数:

// T 是一个类型参数,代表任何类型
fn largest<T>(list: &[T]) -> &T {
    let mut largest = &list[0];

    for item in list {
        if item > largest { // 注意:这里需要 T 实现 PartialOrd trait
            largest = item;
        }
    }

    largest
}

fn main() {
    let number_list = vec![34, 50, 25, 100, 65];
    let result = largest(&number_list);
    println!("The largest number is {}", result);

    let char_list = vec!['y', 'm', 'a', 'q'];
    let result = largest(&char_list);
    println!("The largest char is {}", result);
}

在这个例子中,<T> 声明了一个泛型类型 T。函数 largest 接收一个 &[T](T 类型的切片),并返回一个 &T。编译器会在编译时根据实际调用时传入的类型(如 i32 或 char)生成特定版本的函数代码。这个过程被称为单态化(Monomorphization),它保证了泛型在运行时没有性能开销。

2. 结构体中的泛型

泛型同样可以用于定义结构体,使其字段可以持有多种类型:

// 定义一个可以包含任意类型 x 和 y 坐标的点
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 };
}

如果希望 x 和 y 可以是不同的类型,我们可以使用多个泛型参数:

struct Point<T, U> {
    x: T,
    y: U,
}

fn main() {
    let both_integer = Point { x: 5, y: 10 };
    let integer_and_float = Point { x: 5, y: 4.0 };
}

3. 枚举中的泛型

Rust 标准库中大量使用了泛型枚举,最经典的例子就是 Option<T> 和 Result<T, E>:

enum Option<T> {
    Some(T),
    None,
}

enum Result<T, E> {
    Ok(T),
    Err(E),
}

这使得 Option 可以表示任何类型的值存在或不存在,Result 可以表示任何成功值或错误类型。

特性:共享行为的抽象

特性(Trait)告诉 Rust 编译器一个类型必须实现哪些功能。你可以将其理解为其他语言中的“接口”(Interface)。特性定义了一组方法签名,任何实现了该特性的类型都必须提供这些方法的具体实现。

1. 定义和实现 Trait

让我们定义一个 Summary 特性,它包含一个 summarize 方法:

pub trait Summary {
    fn summarize(&self) -> String;
}

// 为 NewsArticle 类型实现 Summary 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)
    }
}

// 为 Tweet 类型实现 Summary trait
pub struct Tweet {
    pub username: String,
    pub content: String,
    pub reply: bool,
    pub retweet: bool,
}

impl Summary for Tweet {
    fn summarize(&self) -> String {
        format!("{}: {}", self.username, self.content)
    }
}

2. 默认实现

Trait 可以为方法提供默认实现,这样实现该 Trait 的类型可以选择使用默认实现,也可以覆盖它:

pub trait Summary {
    fn summarize(&self) -> String {
        String::from("(Read more...)")
    }
}

// 如果 NewsArticle 不实现 summarize 方法,它将使用默认实现
impl Summary for NewsArticle {}

3. Trait 作为参数

Trait 最重要的用途之一是作为函数参数的约束。你可以指定一个函数只接受实现了特定 Trait 的类型:

// 方式一:使用 impl Trait 语法(适用于简单情况)
pub fn notify(item: &impl Summary) {
    println!("Breaking news! {}", item.summarize());
}

// 方式二:使用 Trait Bound 语法(更通用)
pub fn notify<T: Summary>(item: &T) {
    println!("Breaking news! {}", item.summarize());
}

// 方式三:使用 + 语法指定多个 Trait Bound
pub fn notify(item: &(impl Summary + Display)) { ... }
pub fn notify<T: Summary + Display>(item: &T) { ... }

// 方式四:使用 where 子句(当 Trait Bound 较多时更清晰)
fn some_function<T, U>(t: &T, u: &U) -> i32
where
    T: Display + Clone,
    U: Clone + Debug,
{
    // ...
}

泛型与 Trait 的结合:条件性方法

通过为泛型类型添加 Trait Bound,我们可以实现条件性方法。这意味着一个方法只对实现了某些特定 Trait 的泛型类型实例可用。

回到之前的 Point<T> 结构体,我们可以为它添加一个方法,但这个方法只对 T 实现了 Display Trait 的情况有效:

use std::fmt::Display;

struct Point<T> {
    x: T,
    y: T,
}

impl<T: Display> Point<T> {
    fn display_point(&self) {
        println!("Point: ({}, {})", self.x, self.y);
    }
}

fn main() {
    let p = Point { x: 1, y: 2 };
    p.display_point(); // 可以调用,因为 i32 实现了 Display
}

此外,我们还可以为特定的具体类型实现方法,这被称为特化实现:

impl Point<f64> {
    fn distance_from_origin(&self) -> f64 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}

总结

泛型和特性是 Rust 实现零成本抽象的核心。泛型提供了代码的灵活性,让你能编写一次代码,处理多种类型。特性则提供了行为的契约,让你能定义类型必须遵守的接口。将两者结合使用,你可以创建出既强大又安全的抽象层,这是 Rust 区别于许多其他系统级语言的关键所在。在实际开发中,你会频繁地使用标准库提供的各种 Trait(如 Display, Clone, Iterator 等),并定义自己的 Trait 来构建模块化和可复用的代码。

Last Updated:: 5/9/26, 3:13 PM