Rust: Rust 迭代器(Iterators):惰性数据处理管道

最后更新:2026-08-26

迭代器(Iterator)是 Rust 中"惰性数据处理管道"——它不立即计算结果,而是逐个产出元素,让你用链式调用的方式声明式地处理数据序列。

迭代器就像一条工厂流水线:数据从一端进入,经过一道道工序(过滤、转换、截取、汇总),最终从另一端产出成品。每道工序只做一件事,但组合起来能完成复杂的处理任务。


1. 流水线工厂的故事

(1) 痛苦:用循环处理数据又臭又长

小明 (Xiao Ming) 是 Rust 工厂的流水线主管。他需要处理一批零件数据:

  1. 筛选出所有合格品(偶数编号)
  2. 给每个零件打上两倍标记
  3. 只取前 5 个
  4. 统计总和

他用传统的 for 循环来写:

RUST
fn main() {
    let parts = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    let mut result = vec![];
    let mut count = 0;

    for &part in &parts {
        if part % 2 == 0 {           // 步骤1: 筛选
            let doubled = part * 2;  // 步骤2: 转换
            result.push(doubled);
            count += 1;
            if count == 5 {          // 步骤3: 截取
                break;
            }
        }
    }

    let sum: i32 = result.iter().sum();
    println!("Result: {:?}, Sum: {}", result, sum);
}

代码虽然能运行,但逻辑分散在各处。如果需求变更为"再跳过前2个"或"再取偶数个",就要重写整个循环。

(2) 迭代器管道的方案

用迭代器链式调用重写上面逻辑:

RUST
fn main() {
    let parts = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

    let sum: i32 = parts.iter()
        .filter(|&&n| n % 2 == 0)   // 步骤1: 筛选偶数
        .map(|&n| n * 2)             // 步骤2: 乘以2
        .take(5)                     // 步骤3: 取前5个
        .sum();                      // 步骤4: 求和

    println!("Sum: {}", sum);
}

代码从"你怎么做"(how)变成了"你要什么"(what)。每一行都是独立工序,可以随时插入、删除、调换顺序——就像在流水线上调整工位。


2. 概念图解

以下 Mermaid 图展示 Iterator trait 方法链从 iter()collect() 的完整惰性处理流程:

100%
graph LR
    A["原始数据<br/>1..=20"] --> B["iter()<br/>创建迭代器"]
    B --> C["filter(|n| n%2==0)<br/>筛选偶数"]
    C --> D["map(|n| n*3)<br/>每个元素 ×3"]
    D --> E["skip(2)<br/>跳过前2个"]
    E --> F["take(5)<br/>取前5个"]
    F --> G["collect()<br/>消费器: 触发求值"]

    H["惰性求值: 仅构建管道<br/>不立即计算"] -.-> C
    H -.-> D
    H -.-> E
    H -.-> F
    I["消费器驱动执行<br/>产出最终结果"] -.-> G

    G --> J["结果<br/>[18, 24, 30, 36, 42]"]

3. 你将学到


4. 核心概念

100%
graph TB
    A[迭代器 Iterator] --> B[迭代器适配器<br>Adapter]
    A --> C[消费器 Consumer]

    B --> D["map(|x| x+1) 转换"]
    B --> E["filter(|x| x>0) 过滤"]
    B --> F["take(n) 取前n个"]
    B --> G["skip(n) 跳过n个"]
    B --> H["chain(other) 拼接"]
    B --> I["enumerate() 加索引"]

    C --> J["collect() 收集到集合"]
    C --> K["sum() 求和"]
    C --> L["count() 计数"]
    C --> M["fold(init, fn) 折叠"]
    C --> N["for x in iter 循环"]

    B -.->|"❌ 惰性<br>不调用就不执行"| C
    C --> O["触发求值"]

(1) 迭代器适配器 vs 消费器

特性 迭代器适配器 (Adapter) 消费器 (Consumer)
作用 转换迭代器(从一个迭代器变到另一个) 驱动迭代器并产生最终结果
返回值 实现 Iterator 的新类型 具体值(如 Vec<T>i32usize
惰性 惰性(不立即执行) 贪婪(立即执行)
典型方法 map, filter, take, skip, chain collect, sum, count, fold, for_each
链式位置 中间步骤 末尾步骤
示例 `.map( x

(2) 常用迭代器适配器速查

适配器 作用 示例 结果
map(f) 对每个元素应用 f 转换 [1,2,3].iter().map(|x| x*2) 2, 4, 6
filter(p) 保留满足条件 p 的元素 [1..=5].filter(|x| x%2==0) 2, 4
take(n) 只取前 n 个元素 [1..].take(3) 1, 2, 3
skip(n) 跳过前 n 个元素 [1..=5].skip(2) 3, 4, 5
chain(it) 拼接另一个迭代器 [1,2].iter().chain([3,4].iter()) 1, 2, 3, 4
enumerate() 给每个元素带上索引 (i, val) ['a','b'].iter().enumerate() (0,'a'), (1,'b')
zip(it) 将两个迭代器一一配对 [1,2].iter().zip(['a','b'].iter()) (1,'a'), (2,'b')
rev() 反转迭代器 [1..=3].rev() 3, 2, 1

(3) 常用消费器方法速查

消费器 作用 示例 返回类型 是否贪婪
collect() 收集为集合 iter.collect::<Vec<_>>() B: FromIterator
sum() 求和 iter.sum::<i32>() S: Sum
count() 计数 iter.count() usize
fold(init, f) 累积计算 iter.fold(0, |acc, x| acc + x) 初始值类型
reduce(f) 无初始值折叠 iter.reduce(|a, b| a + b) Option<Item>
for_each(f) 逐个执行(不返回值) iter.for_each(|x| println!(x)) ()
any(p) 是否存在满足条件 iter.any(|x| x > 0) bool
all(p) 是否全部满足条件 iter.all(|x| x > 0) bool
find(p) 找第一个满足条件的 iter.find(|x| *x > 3) Option<Item>
max() / min() 最大/最小值 iter.max() Option<Item>

5. 示例

▶ 示例 1:Iterator trait 与 next() 方法——理解迭代器的本质(难度 ⭐)

RUST
// ============================================
// 手动调用 next() 理解迭代器的运作原理
// ============================================

fn main() {
    let numbers = vec![10, 20, 30, 40, 50];

    // iter() 返回一个迭代器,不会消费向量
    let mut iter = numbers.iter();

    // next() 每次返回 Option<&T>
    // Some(&value) 表示还有下一个元素
    // None 表示迭代结束
    println!("{:?}", iter.next());  // Some(10)
    println!("{:?}", iter.next());  // Some(20)
    println!("{:?}", iter.next());  // Some(30)
    println!("{:?}", iter.next());  // Some(40)
    println!("{:?}", iter.next());  // Some(50)
    println!("{:?}", iter.next());  // None
    println!("{:?}", iter.next());  // None (再次调用仍返回 None)

    // for 循环是 next() 的语法糖
    let mut count = 0;
    let iter2 = numbers.iter();
    for val in iter2 {
        println!("for loop #{}: {}", count, val);
        count += 1;
    }
}

输出:

TEXT 📖 仅展示
Some(10)
Some(20)
Some(30)
Some(40)
Some(50)
None
None
for loop #0: 10
for loop #1: 20
for loop #2: 30
for loop #3: 40
for loop #4: 50

迭代器的核心契约就是 next() 方法:每次调用返回 Some(元素),耗尽后返回 Nonefor 循环就是不断调用 next() 直到遇到 None 的语法糖。理解这一点,就理解了一切迭代器的基础。


▶ 示例 2:迭代器适配器链式调用——map / filter / take / skip(难度 ⭐⭐)

RUST
// ============================================
// 组合多个适配器构建数据处理管道
// ============================================

fn main() {
    // 原始数据:1 到 20
    let data = 1..=20;

    // 管道:筛选偶数 → 乘以3 → 跳过前2个 → 取前5个
    let result: Vec<i32> = data
        .filter(|&n| n % 2 == 0)      // [2,4,6,8,10,12,14,16,18,20]
        .map(|n| n * 3)                // [6,12,18,24,30,36,42,48,54,60]
        .skip(2)                       // [18,24,30,36,42]
        .take(5)                       // [18,24,30,36,42]
        .collect();                    // 触发求值,收集到 Vec

    println!("Pipeline result: {:?}", result);

    // 另一个管道:用 chain 拼接两个切片
    let first = vec!["A", "B", "C"];
    let second = vec!["X", "Y", "Z"];
    let combined: Vec<&str> = first.iter()
        .chain(second.iter())
        .copied()
        .collect();
    println!("Chained: {:?}", combined);

    // 用 enumerate 给元素加索引
    let fruits = vec!["apple", "banana", "cherry"];
    let indexed: Vec<(usize, &str)> = fruits.iter()
        .enumerate()
        .map(|(i, &name)| (i + 1, name))
        .collect();
    println!("Indexed: {:?}", indexed);
}

输出:

TEXT 📖 仅展示
Pipeline result: [18, 24, 30, 36, 42]
Chained: ["A", "B", "C", "X", "Y", "Z"]
Indexed: [(1, "apple"), (2, "banana"), (3, "cherry")]

适配器链就像流水线上的工位排列:每个适配器只做一件事,数据依次流过每个工位。collect() 是管道末端的需求信号——没有它,管道中的工人就不会开始工作(惰性求值)。


▶ 示例 3:自定义迭代器——为你的类型实现 Iterator trait(难度 ⭐⭐)

RUST
// ============================================
// 自定义 Fibonacci 迭代器
// 实现 Iterator trait 让任何类型可以迭代
// ============================================

// Fibonacci 序列生成器
struct Fibonacci {
    current: u64,
    next: u64,
    max: u64,
}

impl Fibonacci {
    fn new(max: u64) -> Self {
        Fibonacci {
            current: 0,
            next: 1,
            max,
        }
    }
}

// 实现 Iterator trait 是自定义迭代器的核心
impl Iterator for Fibonacci {
    // Item 是迭代器产出的元素类型
    type Item = u64;

    // next() 返回 Option<Self::Item>
    // Some(value) 表示还有下一个元素
    // None 表示迭代结束
    fn next(&mut self) -> Option<Self::Item> {
        if self.current > self.max {
            return None;
        }
        let result = self.current;

        // 更新到下一个 Fibonacci 数
        let new_next = self.current + self.next;
        self.current = self.next;
        self.next = new_next;

        Some(result)
    }
}

fn main() {
    println!("Fibonacci up to 50:");
    let fib = Fibonacci::new(50);

    // Fibonacci 现在可以用在 for 循环中
    for (i, n) in fib.enumerate() {
        println!("  fib({}) = {}", i, n);
    }

    // 也可以和适配器链配合使用
    println!("\nEven Fibonacci numbers up to 100:");
    let even_fibs: Vec<u64> = Fibonacci::new(100)
        .filter(|&n| n % 2 == 0)
        .collect();
    println!("{:?}", even_fibs);
}

输出:

TEXT 📖 仅展示
Fibonacci up to 50:
  fib(0) = 0
  fib(1) = 1
  fib(2) = 1
  fib(3) = 2
  fib(4) = 3
  fib(5) = 5
  fib(6) = 8
  fib(7) = 13
  fib(8) = 21
  fib(9) = 34

Even Fibonacci numbers up to 100:
[0, 2, 8, 34]

实现 Iterator trait 只需要做一件事:定义 type Item(元素类型)和 fn next()(产出规则)。一旦实现,你的类型就自动获得了所有适配器方法(mapfiltertake 等)——这就是"鸭子类型"在 Rust 中的体现:实现了 next(),就像迭代器一样用。


▶ 示例 4:消费器实战——fold / sum / count / collect(难度 ⭐⭐⭐)

RUST
// ============================================
// 消费器:驱动迭代器执行并产生最终结果
// ============================================

fn main() {
    let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

    // sum() — 求和
    let total: i32 = numbers.iter().sum();
    println!("Sum: {}", total);

    // count() — 计数
    let cnt = numbers.iter().count();
    println!("Count: {}", cnt);

    // fold() — 通用折叠操作(初始值 + 累加器闭包)
    // 这里计算阶乘 10!
    let factorial: u64 = (1..=10u64).fold(1, |acc, x| acc * x);
    println!("10! = {}", factorial);

    // fold() — 手动实现 sum 和 count
    let sum_via_fold: i32 = numbers.iter().fold(0, |acc, &x| acc + x);
    let count_via_fold: usize = numbers.iter().fold(0, |acc, _| acc + 1);
    println!("Sum (via fold): {}", sum_via_fold);
    println!("Count (via fold): {}", count_via_fold);

    // collect() — 收集到不同类型的集合
    let doubled: Vec<i32> = numbers.iter().map(|&x| x * 2).collect();
    println!("Doubled: {:?}", doubled);

    let even_set: std::collections::HashSet<i32> = numbers.iter()
        .filter(|&&x| x % 2 == 0)
        .copied()
        .collect();
    println!("Even set: {:?}", even_set);

    // 综合:找出前 5 个偶数的平方和
    let complex_result: i32 = (1..=100)
        .filter(|&n| n % 2 == 0)
        .map(|n| n * n)
        .take(5)
        .fold(0, |acc, n| acc + n);
    println!("Sum of first 5 even squares: {}", complex_result);
}

输出:

TEXT 📖 仅展示
Sum: 55
Count: 10
10! = 3628800
Sum (via fold): 55
Count (via fold): 10
Doubled: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
Even set: {2, 4, 6, 8, 10}
Sum of first 5 even squares: 220

消费器是管道末端的关键:sum()count()fold() 直接算出数值结果;collect() 收集到集合中。fold() 是最通用的消费器——sum()count() 本质上都是 fold() 的特化形式。一个管道必须有消费器才会真正执行,否则一切都是空谈。


▶ 示例 5:综合练习——自定义迭代器实现 FizzBuzz 生成器(难度 ⭐⭐⭐)

RUST
// ============================================
// 综合示例:自定义迭代器 + 适配器链
// ============================================

struct FizzBuzz {
    current: u32,
    limit: u32,
}

impl FizzBuzz {
    fn new(limit: u32) -> Self {
        FizzBuzz { current: 0, limit }
    }
}

impl Iterator for FizzBuzz {
    type Item = String;

    fn next(&mut self) -> Option<String> {
        self.current += 1;
        if self.current > self.limit {
            return None;
        }
        let n = self.current;
        let result = match (n % 3, n % 5) {
            (0, 0) => "FizzBuzz".to_string(),
            (0, _) => "Fizz".to_string(),
            (_, 0) => "Buzz".to_string(),
            _ => n.to_string(),
        };
        Some(result)
    }
}

struct Fibonacci {
    curr: u64,
    next: u64,
}

impl Iterator for Fibonacci {
    type Item = u64;
    fn next(&mut self) -> Option<u64> {
        let result = self.curr;
        self.curr = self.next;
        self.next = result + self.next;
        Some(result)
    }
}

fn main() {
    println!("=== FizzBuzz (1-20) ===");
    for item in FizzBuzz::new(20) {
        print!("{} ", item);
    }
    println!();

    let fizz_count = FizzBuzz::new(100)
        .filter(|s| s.starts_with("Fizz"))
        .count();
    println!("1-100 中 Fizz 出现次数: {}", fizz_count);

    println!("\n=== Fibonacci 前 15 项 ===");
    let fib = Fibonacci { curr: 0, next: 1 };
    for val in fib.take(15) {
        print!("{} ", val);
    }
    println!();

    let fib_sum: u64 = Fibonacci { curr: 1, next: 1 }
        .take_while(|&x| x < 1_000_000)
        .filter(|&x| x % 2 == 0)
        .sum();
    println!("Fibonacci < 1M 的偶数和: {}", fib_sum);
}

输出:

TEXT 📖 仅展示
=== FizzBuzz (1-20) ===
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz 

1-100 中 Fizz 出现次数: 27

=== Fibonacci 前 15 项 ===
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 

Fibonacci < 1M 的偶数和: 1089154

自定义迭代器只需实现 Iterator trait 的 next() 方法。FizzBuzz 生成器可链式调用 filter/countFibonacci 无限迭代器用 take/take_while 限制输出,再用 filter/sum 聚合。


❓ 常见问题

Q iter()into_iter()iter_mut() 有什么区别?
A 它们返回不同类型的迭代器。
Q 为什么适配器链不执行代码也不会报错?
A 因为适配器是惰性的(lazy)。
Q collect() 怎么知道要收集到什么类型?
A 通过类型推断。
Q 自定义迭代器的 type Item 是什么?
A Item 是关联类型,指定 next() 返回的 Some 中的类型。
Q fold()reduce() 有什么区别?
A fold() 需要初始值,reduce() 使用第一个元素作为初始值。

📖 小节


📝 作业

  1. 难度 ⭐:用迭代器重写下面这段代码。将 [1, 2, 3, 4, 5, 6, 7, 8] 中所有奇数取出,乘以 10,收集到 Vec 中并打印。

    RUST
    // 用迭代器链替换这个循环
    let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8];
    let mut result = vec![];
    for &n in &numbers {
        if n % 2 == 1 {
            result.push(n * 10);
        }
    }
    println!("{:?}", result);
    
  2. 难度 ⭐⭐:为 struct StepRange { start: i32, end: i32, step: i32 } 实现 Iterator trait,让它能像 for n in StepRange::new(0, 10, 2) 这样使用,输出 0, 2, 4, 6, 8, 10。然后用 map 将每个值平方,用 collect 收集到 Vec 中。

  3. 难度 ⭐⭐⭐:写一个函数 fn word_count(text: &str) -> std::collections::HashMap<String, usize>,使用迭代器方法统计一段文本中每个单词出现的次数。要求:用 split_whitespace() 分割、map 转小写、fold 构建 HashMap。提示:HashMapentry() API 配合 or_insert() 可以方便地计数。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏