Rust 之 迭代器

在 Rust 中,迭代器是处理集合数据(如数组、向量、哈希表等)的核心工具,它提供了一种统一、高效且安全的方式遍历和操作元素。

与其他语言的迭代器不同,Rust 迭代器深度融合了所有权、借用机制,兼具函数式编程的灵活性与系统级语言的性能,是 Rust 零成本抽象理念的典型体现。

本文将从基础概念出发,逐步深入迭代器的用法、特性、拓展场景,结合详细示例帮助大家彻底掌握这一核心特性。

一、迭代器的核心概念

迭代器(Iterator)本质上是一个实现了 std::iter::Iterator trait 的类型,它能够依次产生序列中的元素,并在遍历结束后标记"完成"状态。其核心逻辑由 next() 方法定义,该方法返回 Option<T>

  • 当存在下一个元素时,返回 Some(元素值)

  • 当遍历结束时,返回None

Rust 迭代器的核心优势的是惰性求值:迭代器本身不会执行任何遍历操作,直到调用了"消费型方法"才会触发元素的产生和处理,这种设计能减少不必要的计算,提升性能。

二、迭代器的基本用法

2.1 三种基础迭代器类型

针对集合的不同访问需求,Rust 提供了三种默认迭代器,分别对应 不同的借用 / 所有权 语义:

1. 不可变引用迭代器(iter())

最常用的迭代器,遍历集合时 产生元素 的不可变引用(&T),不获取元素所有权,遍历后集合仍可正常使用。

rust 复制代码
fn main() {
    let vec = vec![1, 2, 3, 4, 5];
    // 创建不可变引用迭代器
    let mut iter = vec.iter();
    
    // 手动调用 next() 遍历
    assert_eq!(iter.next(), Some(&1));
    assert_eq!(iter.next(), Some(&2));
    assert_eq!(iter.next(), Some(&3));
    assert_eq!(iter.next(), Some(&4));
    assert_eq!(iter.next(), Some(&5));
    assert_eq!(iter.next(), None);
    
    // 遍历后集合仍可使用
    println!("vec 长度: {}", vec.len()); // 输出 5
}
2. 可变引用迭代器(iter_mut())

产生元素的 可变引用(&mut T),可在遍历过程中 修改集合元素,同样不获取所有权。

rust 复制代码
fn main() {
    let mut vec = vec![1, 2, 3, 4, 5];
    // 创建可变引用迭代器
    let mut iter_mut = vec.iter_mut();
    
    // 遍历并修改元素
    if let Some(num) = iter_mut.next() {
        *num = 10; // 通过可变引用修改值
    }
    if let Some(num) = iter_mut.next() {
        *num = 20;
    }
    
    println!("修改后的 vec: {:?}", vec); // 输出 [10, 20, 3, 4, 5]
}
3. 所有权转移迭代器(into_iter())

产生元素的所有权(T),遍历后 原 集合 会被消耗(所有权转移给迭代器),无法再被访问。

rust 复制代码
fn main() {
    let vec = vec![1, 2, 3, 4, 5];
    // 创建所有权转移迭代器,vec 所有权被转移
    let mut into_iter = vec.into_iter();
    
    assert_eq!(into_iter.next(), Some(1));
    assert_eq!(into_iter.next(), Some(2));
    
    // 此时 vec 已被消耗,无法再使用
    // println!("vec: {:?}", vec); // 编译错误:value borrowed here after move
}

2.2 for 循环与迭代器的关联

Rust 中的 for 循环本质上是迭代器的语法糖,它会自动调用集合的 into_iter()(或根据上下文适配 iter()/iter_mut()),并循环调用 next() 直到返回 None

rust 复制代码
fn main() {
    let vec = vec![1, 2, 3, 4, 5];
    // 等价于 vec.iter() 遍历,因为 for 循环默认借用集合
    for num in &vec {
        println!("{}", num);
    }
    
    let mut vec_mut = vec![1, 2, 3];
    // 可变遍历
    for num in &mut vec_mut {
        *num *= 2;
    }
    println!("{:?}", vec_mut); // 输出 [2, 4, 6]
    
    // 所有权转移遍历
    for num in vec {
        println!("{}", num);
    }
    // vec 已被消耗,无法再使用
}

三、迭代器适配器:链式操作的精髓

迭代器适配器(Iterator Adapter)是一类方法,它接收一个迭代器,返回一个新的迭代器。

用于对元素进行转换、过滤、截取等操作。适配器同样遵循惰性求值,只有当后续调用消费型方法时,所有适配器的逻辑才会被执行。常见的适配器包括 mapfiltertakeskip 等,且支持链式调用。

3.1 常用适配器示例

rust 复制代码
fn main() {
    let vec = vec![1, 2, 3, 4, 5, 6, 7, 8];
    
    // 1. map:转换元素,将每个元素乘 2
    let mapped: Vec<i32> = vec.iter().map(|&x| x * 2).collect();
    println!("map 结果: {:?}", mapped); // 输出 [2, 4, 6, 8, 10, 12, 14, 16]
    
    // 2. filter:过滤元素,保留偶数
    let filtered: Vec<i32> = vec.iter().filter(|&&x| x % 2 == 0).cloned().collect();
    println!("filter 结果: {:?}", filtered); // 输出 [2, 4, 6, 8]
    
    // 3. take(n):截取前 n 个元素,超出长度则取全部
    let taken: Vec<i32> = vec.iter().take(3).cloned().collect();
    println!("take 结果: {:?}", taken); // 输出 [1, 2, 3]
    
    // 4. skip(n):跳过前 n 个元素,不足则返回空
    let skipped: Vec<i32> = vec.iter().skip(5).cloned().collect();
    println!("skip 结果: {:?}", skipped); // 输出 [6, 7, 8]
    
    // 5. 链式调用:组合多个适配器
    let result: Vec<i32> = vec.iter()
        .filter(|&&x| x > 3) // 保留大于 3 的元素
        .map(|&x| x * 3)      // 乘 3
        .take(2)               // 取前 2 个
        .collect();
    println!("链式调用结果: {:?}", result); // 输出 [12, 15](4*3=12,5*3=15)
}

注意:collect() 是典型的消费型方法,它将迭代器产生的元素收集到指定集合中。由于 Rust 无法自动推断集合类型,需显式标注类型(如 Vec<i32>)。

3.2 适配器的惰性求值验证

为了验证惰性求值,我们可以在适配器中加入打印逻辑,观察其执行时机:

rust 复制代码
fn main() {
    let vec = vec![1, 2, 3];
    // 仅创建适配器链,未调用消费方法,无任何输出
    let adapter_chain = vec.iter().map(|&x| {
        println!("处理元素: {}", x);
        x * 2
    });
    
    println!("开始消费迭代器");
    // 调用 collect() 消费,此时才执行 map 逻辑
    let result: Vec<i32> = adapter_chain.collect();
    println!("结果: {:?}", result);
}

输出结果:

Plain 复制代码
开始消费迭代器
处理元素: 1
处理元素: 2
处理元素: 3
结果: [2, 4, 6]

可见,适配器逻辑仅在消费时执行,避免了无效计算。

四、消费型迭代器方法

消费型方法(Consumer)是触发迭代器执行的核心,它会消耗迭代器,返回一个非迭代器的值(如集合、数值、布尔值等)。除了前面提到的 collect(),还有以下常用消费方法:

4.1 常用消费方法示例

rust 复制代码
fn main() {
    let vec = vec![1, 2, 3, 4, 5];
    let iter = vec.iter().cloned();
    
    // 1. count():统计元素个数
    let count = iter.clone().count();
    println!("元素个数: {}", count); // 输出 5
    
    // 2. sum():计算元素总和(需元素实现 Sum trait)
    let sum: i32 = iter.clone().sum();
    println!("总和: {}", sum); // 输出 15
    
    // 3. fold():折叠操作,接收初始值和闭包,累计计算结果
    // 计算 1*2*3*4*5 = 120
    let product: i32 = iter.clone().fold(1, |acc, x| acc * x);
    println!("乘积: {}", product); // 输出 120
    
    // 4. for_each():对每个元素执行闭包(类似 for 循环,无返回值)
    println!("for_each 处理元素:");
    iter.clone().for_each(|x| println!("{}", x));
    
    // 5. any():判断是否存在满足条件的元素,找到后立即终止遍历(短路求值)
    let has_even = iter.clone().any(|x| x % 2 == 0);
    println!("是否存在偶数: {}", has_even); // 输出 true
    
    // 6. all():判断所有元素是否都满足条件,不满足则立即终止
    let all_gt_zero = iter.clone().all(|x| x > 0);
    println!("所有元素都大于 0: {}", all_gt_zero); // 输出 true
    
    // 7. find():查找第一个满足条件的元素,返回 Option<T>
    let first_gt_3 = iter.find(|&x| x > 3);
    println!("第一个大于 3 的元素: {:?}", first_gt_3); // 输出 Some(4)
}

其中,fold() 是功能最强大的消费方法之一,sum()count() 等方法本质上都是基于 fold() 实现的。

五、迭代器拓展:自定义迭代器与性能分析

5.1 自定义迭代器

只要实现 std::iter::Iterator trait,就能自定义迭代器。该 trait 仅需强制实现 next() 方法,其他方法(如 mapfiltercount 等)都有默认实现,无需手动编写。

示例:自定义一个生成斐波那契数列的迭代器,生成前 n 项:

rust 复制代码
// 定义斐波那契迭代器结构体,存储当前状态
struct Fibonacci {
    a: u32,
    b: u32,
    remaining: usize, // 剩余生成次数
}

// 实现构造方法,初始化迭代器
impl Fibonacci {
    fn new(count: usize) -> Self {
        Fibonacci {
            a: 0,
            b: 1,
            remaining: count,
        }
    }
}

// 实现 Iterator trait
impl Iterator for Fibonacci {
    type Item = u32; // 迭代器产生的元素类型

    // 核心方法:生成下一个元素
    fn next(&mut self) -> Option<Self::Item> {
        if self.remaining == 0 {
            return None;
        }
        let result = self.a;
        // 更新斐波那契状态:下一项 = a + b,a 变为原来的 b,b 变为新值
        let next_b = self.a + self.b;
        self.a = self.b;
        self.b = next_b;
        self.remaining -= 1;
        Some(result)
    }
}

fn main() {
    // 创建斐波那契迭代器,生成前 10 项
    let fib = Fibonacci::new(10);
    // 消费迭代器并收集结果
    let fib_vec: Vec<u32> = fib.collect();
    println!("斐波那契前 10 项: {:?}", fib_vec);
    // 输出:[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
}

5.2 迭代器的性能优势

Rust 迭代器是"零成本抽象",即编译后生成的汇编代码与手动编写的循环几乎一致,性能无损耗。这是因为:

  • 迭代器的方法均为内联函数,编译器可进行充分优化;

  • 惰性求值避免了中间集合的创建,减少内存开销;

  • 迭代器不依赖运行时反射,所有类型检查在编译期完成。

对比手动循环与迭代器的性能(以遍历数组求和为例):

rust 复制代码
fn main() {
    let arr = [1; 1_000_000]; // 100 万个 1 的数组
    
    // 手动循环求和
    let mut manual_sum = 0;
    for &num in &arr {
        manual_sum += num;
    }
    
    // 迭代器求和
    let iter_sum: i32 = arr.iter().sum();
    
    assert_eq!(manual_sum, iter_sum);
}

通过 cargo asm 查看汇编代码可发现,两者的指令完全一致,性能毫无差异。

5.3 迭代器与所有权的深度结合

Rust 迭代器的安全性源于与所有权机制的结合,例如:

  • 不可变迭代器(iter())持有集合的不可变借用,期间无法修改集合;

  • 可变迭代器(iter_mut())持有集合的可变借用,期间无法同时创建其他借用;

  • 所有权转移迭代器(into_iter())确保集合仅被消费一次,避免悬垂引用。

这种设计从编译期杜绝了数据竞争和非法访问,是 Rust 安全特性的重要体现。

六、总结

Rust 迭代器是集合处理的核心工具,其惰性求值、零成本抽象、安全语义的特性,使其既能写出简洁优雅的函数式代码,又能保证系统级的性能和安全性。掌握迭代器的关键在于:

  • 理解三种基础迭代器的所有权语义,根据场景选择合适的迭代方式;

  • 灵活运用适配器组合操作,实现链式调用简化逻辑;

  • 熟悉消费型方法,触发迭代器执行并获取结果;

  • 掌握自定义迭代器的方法,适配复杂业务场景。

在实际开发中,迭代器常与 OptionResult 等类型结合(如 Option::iter()Result::iter()),进一步拓展其应用场景。合理使用迭代器,能显著提升代码的可读性、安全性和性能。

相关推荐
yesyesido23 分钟前
智能文件格式转换器:文本/Excel与CSV无缝互转的在线工具
开发语言·python·excel
_200_25 分钟前
Lua 流程控制
开发语言·junit·lua
环黄金线HHJX.26 分钟前
拼音字母量子编程PQLAiQt架构”这一概念。结合上下文《QuantumTuan ⇆ QT:Qt》
开发语言·人工智能·qt·编辑器·量子计算
王夏奇26 分钟前
python在汽车电子行业中的应用1-基础知识概念
开发语言·python·汽车
He_Donglin27 分钟前
Python图书爬虫
开发语言·爬虫·python
星融元asterfusion36 分钟前
AsterNOS SONiC基于YANG模型的现代网络管理:从CLI到gNMI的演进
开发语言·sonic·yang
web3.088899938 分钟前
1688商品详情API接口深度解析
开发语言·python
欧阳天风43 分钟前
用setTimeout代替setInterval
开发语言·前端·javascript
散峰而望44 分钟前
【算法竞赛】顺序表和vector
c语言·开发语言·数据结构·c++·人工智能·算法·github
小鸡脚来咯1 小时前
Java字符串详解
java·开发语言