章节目录

迭代器、泛型、trait 与 API 设计

本章目标

这一章把前面造轮子时用到的抽象串起来:迭代器表达遍历,泛型表达类型参数,trait 表达能力约束,API 设计表达你希望调用者怎么使用代码。

它是什么

迭代器是逐个产生值的对象。trait 是一组行为约定。泛型让代码适用于多种类型。

fn print_all<T: std::fmt::Display>(items: &[T]) {
    for item in items {
        println!("{item}");
    }
}

T: Display 表示元素必须能被格式化显示。

为什么需要

没有泛型,你要为 Stack<i32>Stack<String> 写两套代码。没有 trait,你无法表达“这个类型必须能比较”“这个类型必须能哈希”“这个类型必须能显示”。

Rust 的标准库大量使用这些抽象,让代码既通用又静态安全。

怎么使用

为自定义类型实现 trait:

struct Chapter {
    title: String,
}

impl std::fmt::Display for Chapter {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.title)
    }
}

实现后,Chapter 就可以用 {} 打印。

逐行解释

fn find_first<T, F>(items: &[T], mut predicate: F) -> Option<&T>
where
    F: FnMut(&T) -> bool,
{
    for item in items {
        if predicate(item) {
            return Some(item);
        }
    }
    None
}
  • T 是元素类型。
  • F 是函数或闭包类型。
  • items: &[T] 借用一段元素切片。
  • mut predicate: F 表示闭包调用时可能修改自己捕获的状态。
  • where F: FnMut(&T) -> bool 约束闭包能接收 &T 并返回布尔值。
  • for item in items 遍历切片。
  • 命中条件时返回 Some(item)
  • 全部不命中返回 None

这个函数展示了 API 设计的力量:调用者决定匹配条件,函数负责遍历流程。

常见坑

  • 泛型不是越多越好。先从具体类型写清楚,再提取共性。
  • trait bound 应该放在真正需要能力的地方。
  • 返回迭代器时可能需要 impl Iterator<Item = T>
  • API 参数能借用就不要强行拿所有权,除非函数确实要保存或消费数据。

练习

  1. Stack<T> 添加 iter(&self),返回内部 Vec 的迭代器。
  2. 写一个泛型函数,打印所有实现 Debug 的元素。
  3. 写一个函数接收闭包过滤数字。
  4. 比较 FnFnMutFnOnce 的使用场景。

造轮子任务

把前面的 Stack<T> 做成更像标准库的 API:实现 DefaultFrom<Vec<T>>,添加 into_vec(self) -> Vec<T>,并为不可变遍历提供 iter()。重点是设计调用者舒服的接口。

小结

迭代器、泛型和 trait 是 Rust 抽象能力的核心。它们让你在不牺牲类型安全的前提下写通用代码。好的 API 不只是能用,还要让所有权、借用和错误语义一眼可见。