首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >17-Rust 教程 - 迭代器详解

17-Rust 教程 - 迭代器详解

作者头像
LarryLan
发布2026-04-28 12:25:35
发布2026-04-28 12:25:35
190
举报

迭代器详解

数据处理的流水线:让集合为你打工

🎬 引入

还记得你第一次写循环处理数组的时候吗?大概是这样:

代码语言:javascript
复制
let numbers = vec![, , , , ];
let mut result = Vec::new();

for i in ..numbers.len() {
    if numbers[i] %  ==  {
        result.push(numbers[i] * );
    }
}

是不是感觉……有点啰嗦?而且容易出错(索引越界警告在看着你)。

Rust 的**迭代器(Iterator)**模式让你用更优雅的方式处理集合数据。今天咱们就聊聊这个让代码像流水线一样流畅的神器。

📌 核心概念

什么是迭代器?

迭代器是一个按需产生值的对象。它不会一次性把所有数据算出来,而是你要一个,它给一个。

生活化类比:

想象你去自助餐厅:

  • Vec:像一盘端到你面前的菜,全部都在那里
  • 迭代器:像现做的档口,你点一个,厨师做一个

迭代器的核心 Trait

所有迭代器都实现 Iterator 这个 Trait:

代码语言:javascript
复制
trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
}

说人话:

迭代器只有一个核心方法 next()

  • 有下一个值?返回 Some(value)
  • 没值了?返回 None

就这么简单!

迭代器的三大特性

特性

说明

好处

惰性

不调用就不会执行

节省内存和计算

零成本

编译后和手写循环一样快

没有性能损失

可组合

可以链式调用

代码简洁优雅

生活化类比:

迭代器就像工厂流水线

  • 原料(数据)从一头进去
  • 经过多道工序(filter、map 等)
  • 成品(结果)从另一头出来
  • 中间不囤货(惰性求值)

💻 代码示例

基础示例:创建迭代器

代码语言:javascript
复制
fn main() {
    let numbers = vec![, , , , ];
    
    // 三种创建迭代器的方式
    let iter1 = numbers.iter();      // 不可变引用
    let iter2 = numbers.iter_mut();  // 可变引用
    let iter3 = numbers.into_iter(); // 拥有所有权
    
    for num in iter1 {
        println!("{}", num);  // 只读
    }
}

三种方式的区别:

  • iter():借来看看,不能改
  • iter_mut():借来改改,原数据会变
  • into_iter():拿走,原数据没了

常用适配器:filter

代码语言:javascript
复制
fn main() {
    let numbers = vec![, , , , , , , , , ];
    
    // 过滤出偶数
    let evens: Vec<_> = numbers
        .iter()
        .filter(|&n| n %  == )
        .collect();
    
    println!("{:?}", evens);  // [2, 4, 6, 8, 10]
}

注意: filter 本身不执行,直到你调用 collect() 才真正开始计算(惰性求值)。

常用适配器:map

代码语言:javascript
复制
fn main() {
    let numbers = vec![, , , , ];
    
    // 每个数平方
    let squares: Vec<_> = numbers
        .iter()
        .map(|&n| n * n)
        .collect();
    
    println!("{:?}", squares);  // [1, 4, 9, 16, 25]
}

链式调用:filter + map + collect

代码语言:javascript
复制
fn main() {
    let numbers = vec![, , , , , , , , , ];
    
    // 过滤偶数 -> 平方 -> 收集
    let result: Vec<_> = numbers
        .iter()
        .filter(|&n| n %  == )      // 只留偶数
        .map(|&n| n * n)              // 平方
        .collect();
    
    println!("{:?}", result);  // [4, 16, 36, 64, 100]
}

看到没?代码像读句子一样自然!

其他常用适配器

代码语言:javascript
复制
fn main() {
    let numbers = vec![, , , , ];
    
    // take: 取前 N 个
    let first_three: Vec<_> = numbers
        .iter()
        .take()
        .collect();
    println!("{:?}", first_three);  // [1, 2, 3]
    
    // skip: 跳过前 N 个
    let skip_two: Vec<_> = numbers
        .iter()
        .skip()
        .collect();
    println!("{:?}", skip_two);  // [3, 4, 5]
    
    // enumerate: 带索引
    for (index, value) in numbers.iter().enumerate() {
        println!("{}: {}", index, value);
    }
    
    // zip: 两个迭代器配对
    let letters = vec!['a', 'b', 'c'];
    let paired: Vec<_> = numbers.iter().zip(letters.iter()).collect();
    println!("{:?}", paired);  // [(1, 'a'), (2, 'b'), (3, 'c')]
}

消费器:真正执行的地方

代码语言:javascript
复制
fn main() {
    let numbers = vec![, , , , ];
    
    // collect: 收集到集合
    let doubled: Vec<_> = numbers.iter().map(|n| n * ).collect();
    
    // sum: 求和
    let sum: i32 = numbers.iter().sum();
    println!("总和:{}", sum);  // 15
    
    // product: 求积
    let product: i32 = numbers.iter().product();
    println!("乘积:{}", product);  // 120
    
    // count: 计数
    let count = numbers.iter().filter(|&n| n %  == ).count();
    println!("偶数个数:{}", count);  // 2
    
    // any: 是否有任何一个满足条件
    let has_even = numbers.iter().any(|&n| n %  == );
    println!("有偶数吗?{}", has_even);  // true
    
    // all: 是否全部满足条件
    let all_positive = numbers.iter().all(|&n| n > );
    println!("都是正数吗?{}", all_positive);  // true
    
    // find: 找到第一个满足条件的
    let first_even = numbers.iter().find(|&n| n %  == );
    println!("第一个偶数:{:?}", first_even);  // Some(2)
}

适配器 vs 消费器:

  • 适配器(filter、map、take...):返回新的迭代器,不执行
  • 消费器(collect、sum、count...):真正执行迭代,产生结果

🐛 常见坑点

坑点 1:迭代器被消耗

代码语言:javascript
复制
fn main() {
    let numbers = vec![, , , , ];
    let iter = numbers.iter();
    
    let sum: i32 = iter.sum();  // ✅ 第一次使用
    let count = iter.count();   // ❌ 编译错误!
    // 错误信息:use of moved value: `iter`
}

编译器在说什么人话?

"迭代器已经被 sum() 用掉了(调用了 next() 直到 None),不能再用了!"

解决方案:

代码语言:javascript
复制
// 重新创建迭代器
let sum: i32 = numbers.iter().sum();
let count = numbers.iter().count();

坑点 2:类型推断问题

代码语言:javascript
复制
fn main() {
    let numbers = vec![, , , , ];
    
    // ❌ 编译错误:无法推断类型
    let result = numbers.iter().map(|n| n * ).collect();
}

解决方案:

代码语言:javascript
复制
// 显式指定类型
let result: Vec<_> = numbers.iter().map(|n| n * ).collect();

坑点 3:闭包捕获问题

代码语言:javascript
复制
fn main() {
    let threshold = ;
    let numbers = vec![, , , , , , , , , ];
    
    // ✅ 这样写可以
    let result: Vec<_> = numbers
        .iter()
        .filter(|&n| n > &threshold)
        .collect();
    
    println!("{:?}", result);  // [6, 7, 8, 9, 10]
}

如果闭包要修改捕获的变量,记得用 mutFnMut

坑点 4:生命周期问题

代码语言:javascript
复制
fn main() {
    let numbers = vec![, , , , ];
    let iter = numbers.iter();
    
    // ❌ 这样写不行
    drop(numbers);  // numbers 被删除了
    
    for n in iter {  // ❌ 编译错误:借用已释放的数据
        println!("{}", n);
    }
}

记住: 迭代器只是借用,原数据必须活着。

🎯 实战案例

案例 1:数据处理流水线

代码语言:javascript
复制
fn main() {
    let transactions = vec![
        ("Alice", ),
        ("Bob", ),
        ("Charlie", ),
        ("Alice", ),
        ("Bob", ),
    ];
    
    // 计算每个人的总金额
    use std::collections::HashMap;
    
    let mut totals: HashMap<&str, i32> = HashMap::new();
    
    for &(name, amount) in &transactions {
        *totals.entry(name).or_insert() += amount;
    }
    
    for (name, total) in &totals {
        println!("{}: {}", name, total);
    }
}

案例 2:链式处理复杂数据

代码语言:javascript
复制
#[derive(Debug)]
struct Product {
    name: String,
    price: f64,
    in_stock: bool,
}

fn main() {
    let products = vec![
        Product { name: String::from("Laptop"), price: 999.99, in_stock: true },
        Product { name: String::from("Mouse"), price: 29.99, in_stock: false },
        Product { name: String::from("Keyboard"), price: 79.99, in_stock: true },
        Product { name: String::from("Monitor"), price: 299.99, in_stock: true },
    ];
    
    // 有货的产品 -> 按价格排序 -> 取前 2 个 -> 收集名字
    let cheap_products: Vec<_> = products
        .iter()
        .filter(|p| p.in_stock)
        .map(|p| (p.name.as_str(), p.price))
        .collect();
    
    println!("{:?}", cheap_products);
}

案例 3:自定义迭代器

代码语言:javascript
复制
// 斐波那契数列迭代器
struct Fibonacci {
    current: u32,
    next: u32,
}

impl Fibonacci {
    fn new() -> Fibonacci {
        Fibonacci { current: , next:  }
    }
}

impl Iterator for Fibonacci {
    type Item = u32;
    
    fn next(&mut self) -> Option<Self::Item> {
        let current = self.current;
        self.current = self.next;
        self.next = current + self.next;
        Some(current)
    }
}

fn main() {
    let fib = Fibonacci::new();
    
    // 取前 10 个斐波那契数
    let first_ten: Vec<_> = fib.take().collect();
    println!("{:?}", first_ten);
    // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
}

🧠 思维导图

17-迭代器详解
17-迭代器详解

📝 小结

金句回顾:

  1. 迭代器是惰性的——不调用就不执行
  2. 适配器不执行,消费器才执行
  3. 链式调用让代码像读句子
  4. 零成本抽象——编译后和手写循环一样快
  5. 迭代器只能用一次——用完就没了

下篇预告:

咱们已经学了模式匹配的基础(match 和 if let),但那只是开胃菜。下篇聊聊模式匹配进阶,看看 Rust 的模式匹配能玩出什么花样——解构、守卫、嵌套模式,让你写出像诗歌一样的代码!

🔗 参考资料

  • Rust Book - 迭代器
  • Iterator Trait 文档
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-04-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Larry的Hub 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 迭代器详解
    • 🎬 引入
    • 📌 核心概念
      • 什么是迭代器?
      • 迭代器的核心 Trait
      • 迭代器的三大特性
    • 💻 代码示例
      • 基础示例:创建迭代器
      • 常用适配器:filter
      • 常用适配器:map
      • 链式调用:filter + map + collect
      • 其他常用适配器
      • 消费器:真正执行的地方
    • 🐛 常见坑点
      • 坑点 1:迭代器被消耗
      • 坑点 2:类型推断问题
      • 坑点 3:闭包捕获问题
      • 坑点 4:生命周期问题
    • 🎯 实战案例
      • 案例 1:数据处理流水线
      • 案例 2:链式处理复杂数据
      • 案例 3:自定义迭代器
    • 🧠 思维导图
    • 📝 小结
    • 🔗 参考资料
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档