Skip to content

迭代器

迭代器是 Rust 中处理序列数据的核心抽象。与 C 风格的 for (i = 0; i < len; i++) 循环不同,Rust 的迭代器不需要手动管理索引、边界检查或循环状态——你只需描述"对每个元素做什么",而不是"怎么遍历"。这种抽象不仅让代码更简洁,而且由于零成本抽象原则,迭代器在运行时与等价的手写循环效率相同甚至更高。

迭代器是惰性的: 创建一个迭代器不会执行任何操作,只有调用消费它的方法时,迭代逻辑才真正运行。

Iterator trait 与 next 方法

一个类型只要实现了 Iterator trait,它就是迭代器

迭代器的核心定义来自标准库中的 Iterator trait:

rust
pub trait Iterator {
    type Item;                                 // 关联类型: 每次"吐出"的元素类型
    fn next(&mut self) -> Option<Self::Item>;  // 核心方法: 取下一个元素
    // ... 数十个有默认实现的方法(map、filter、sum 等)
}

只要实现了 next 方法,编译器就会自动"赠送"你几十个方法——mapfiltersumcollect 等都是标准库基于 next 提供的默认实现。

手动调用 next 方法时,必须将迭代器声明为 mut,因为每次调用都会修改迭代器内部记录的"当前位置":

rust
fn main() {
    let arr = [1, 2, 3];
    let mut arr_iter = arr.into_iter();

    assert_eq!(arr_iter.next(), Some(1));
    assert_eq!(arr_iter.next(), Some(2));
    assert_eq!(arr_iter.next(), Some(3));
    assert_eq!(arr_iter.next(), None);   // 迭代结束
}

每次调用 next 都会消耗一个元素。迭代完成后只能得到 None,无法"倒回"。

for 循环与迭代器

Rust 的 for 循环只是迭代器的语法糖,编译器会将它展开为对迭代器 next 方法的循环调用:

rust
let values = vec![1, 2, 3];

for v in values {
    println!("{}", v);
}

// for 循环的本质展开形式:
{
    let result = match IntoIterator::into_iter(values) {
        mut iter => loop {
            match iter.next() {
                Some(x) => println!("{}", x),
                None => break,
            }
        },
    };
    result
}

这解释了一个常见问题: 数组本身不是迭代器,但可以在 for 循环中直接使用。原因在于数组实现了 IntoIterator trait——for 循环会自动调用 into_iter() 将其转换为迭代器。

IteratorIntoIterator 的关系:

Trait含义必须实现的方法
Iterator就是迭代器,能调用 nextnext()
IntoIterator能转换为迭代器into_iter()

迭代器本身也实现了 IntoIterator(直接返回自身),因此以下写法完全合法:

rust
let values = vec![1, 2, 3];
// 无论写多少个 into_iter(),都能正常编译并运行
for v in values.into_iter().into_iter().into_iter() {
    println!("{}", v);
}

产生迭代器的三种方式

大多数集合类型提供三种方法来产生迭代器,区别在于对原集合所有权的处理:

方法产生元素类型对原集合的影响等价的 for 写法
iter()&T借用,原集合继续有效for x in &vec
iter_mut()&mut T可变借用,可原地修改for x in &mut vec
into_iter()T转移所有权,原集合失效for x in vec
rust
// iter(): 不可变借用,原集合依然有效
let v = vec![1, 2, 3];
let count = v.iter().count();
println!("v 依然可用: {:?}", v);  // ✅

// iter_mut(): 可变借用,原地修改元素
let mut v1 = vec![1, 2, 3];
v1.iter_mut().for_each(|x| *x *= 2);
println!("原地翻倍: {:?}", v1);  // [2, 4, 6]

// into_iter(): 转移所有权
let v2 = vec![1, 2, 3];
let v2_owned: Vec<i32> = v2.into_iter().map(|x| x + 10).collect();
// println!("{:?}", v2);  // ❌ v2 已被移动

能产生迭代器的常见类型

几乎所有"装着一堆东西"的类型都可以转换为迭代器:

  • 集合类型: Vec<T>HashMap<K,V>BTreeMap<K,V>HashSet<T>、原生数组 [T; N]、切片 &[T]
  • 字符串: .chars()(Unicode 字符)、.bytes()(原始字节)、.lines()(按行)
  • 范围: (0..10)(0..=10) 本身就是迭代器
  • Option / Result: Some(5).iter() 产生只吐出一次 5 的迭代器
  • IO 流: BufReader::lines() 惰性按行读取文件,不需要一次性加载到内存

迭代器适配器

迭代器适配器接收一个迭代器,返回一个新的迭代器,本身不做任何计算(惰性)。必须在最后调用消费适配器才会触发实际执行:

rust
let v1: Vec<i32> = vec![1, 2, 3];
v1.iter().map(|x| x + 1);
// ⚠️ 编译器警告: unused `Map` that must be used
// 提示: iterators are lazy and do nothing unless consumed

加上消费适配器 collect 后才真正执行:

rust
let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();  // ✅ [2, 3, 4]

常用迭代器适配器:

方法说明
map(f)对每个元素做变换,返回新迭代器
filter(p)保留满足条件的元素
enumerate()带索引遍历,得到 (index, item)
zip(other)将两个迭代器压缩成 (a, b) 元组迭代器
chain(other)连接两个迭代器,顺序遍历
flat_map(f)map 后展平一层(等价于 .map(f).flatten())
flatten()展平嵌套迭代器(Vec<Vec<T>> → 单层)
take(n)只取前 n 个元素
skip(n)跳过前 n 个元素
peekable()可以 .peek() 而不消耗元素
cloned()&T 迭代器转为 T 迭代器(需 Clone)
copied()&T 迭代器转为 T 迭代器(需 Copy)

闭包可以捕获外部环境,这是迭代器适配器最强大的特性之一:

rust
struct Shoe {
    size: u32,
    style: String,
}

fn shoes_in_size(shoes: Vec<Shoe>, shoe_size: u32) -> Vec<Shoe> {
    shoes.into_iter().filter(|s| s.size == shoe_size).collect()
    //                              ^^^^^^^^^^^^^^^ 捕获了外部变量 shoe_size
}

链式调用示例:

rust
let v = vec![1, 2, 3, 4, 5, 6];

// enumerate + filter + map 链式组合
let result: Vec<String> = v.iter()
    .enumerate()
    .filter(|(_, x)| *x % 2 == 0)
    .map(|(i, x)| format!("索引{i}: {x}"))
    .collect();

// zip: 合并两个迭代器
use std::collections::HashMap;
let names = ["sunface", "sunfei"];
let ages  = [18u32, 20];
let folks: HashMap<_, _> = names.into_iter().zip(ages.into_iter()).collect();
// {"sunface": 18, "sunfei": 20}

// flat_map: 展开嵌套
let words = vec!["hello world", "foo bar"];
let tokens: Vec<&str> = words.iter().flat_map(|s| s.split(' ')).collect();
// ["hello", "world", "foo", "bar"]

消费适配器

消费适配器在内部调用 next 方法,获取迭代器的所有权并消耗它,最终返回一个具体的值:

rust
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter();
let total: i32 = v1_iter.sum();  // sum 获取了 v1_iter 的所有权

println!("{:?}", v1);       // ✅ v1 只是被借用,仍可使用
// println!("{:?}", v1_iter);  // ❌ v1_iter 已被 sum 消耗

常用消费适配器:

方法说明
collect::<Vec<_>>()收集成集合(Vec、HashMap 等)
count()元素总数
sum() / product()求和 / 求积
any(p) / all(p)任意 / 全部满足条件 → bool
find(p)首个满足条件的元素 → Option<&T>
position(p)首个满足条件的索引 → Option<usize>
max() / min()最大 / 最小值 → Option<T>
fold(init, f)折叠累积(类似 reduce)
for_each(f)遍历并执行副作用
rust
let v = vec![1, 2, 3, 4, 5];

let sum: i32      = v.iter().sum();                       // 15
let has_even      = v.iter().any(|x| x % 2 == 0);        // true
let all_pos       = v.iter().all(|x| *x > 0);            // true
let first_gt3     = v.iter().find(|&&x| x > 3);          // Some(&4)
let total         = v.iter().fold(0, |acc, x| acc + x);  // 15

// enumerate 获取索引
let v2 = vec![1u64, 2, 3, 4, 5, 6];
for (i, val) in v2.iter().enumerate() {
    println!("第{}个值是{}", i, val);
}

// enumerate + filter: 每隔一个元素求和
let result = v2.iter()
    .enumerate()
    .filter(|&(idx, _)| idx % 2 == 0)
    .map(|(_, val)| val)
    .fold(0u64, |sum, x| sum + x);  // 1 + 3 + 5 = 9

自定义迭代器

只要为自己的类型实现 Iterator trait,它就能享用所有迭代器适配器。只需实现一个 next 方法即可,其余方法编译器自动赠送:

rust
struct Counter {
    count: u32,
}

impl Counter {
    fn new() -> Counter {
        Counter { count: 0 }
    }
}

// 实现 Iterator trait
impl Iterator for Counter {
    type Item = u32;

    fn next(&mut self) -> Option<Self::Item> {
        if self.count < 5 {
            self.count += 1;
            Some(self.count)
        } else {
            None
        }
    }
}

fn main() {
    // 手动调用 next
    let mut counter = Counter::new();
    assert_eq!(counter.next(), Some(1));
    assert_eq!(counter.next(), Some(2));
    assert_eq!(counter.next(), Some(3));
    assert_eq!(counter.next(), Some(4));
    assert_eq!(counter.next(), Some(5));
    assert_eq!(counter.next(), None);

    // for 循环也可直接使用(Iterator 自动实现了 IntoIterator)
    for item in Counter::new() {
        println!("item = {}", item);
    }
}

实现了 next 之后,所有标准库方法都可以直接用。下面的示例仅实现了 next,却能链式调用 zipmapfiltersum:

rust

// 假设 Counter::new() 产生 [1, 2, 3, 4, 5]
// skip(1) 后产生 [2, 3, 4, 5]
// zip 合并为 [(1,2),(2,3),(3,4),(4,5)]
// map 相乘为 [2, 6, 12, 20]
// filter 取 3 的倍数 [6, 12]
// sum = 18
let sum: u32 = Counter::new()    // 产生 [1, 2, 3, 4, 5]
    .zip(Counter::new().skip(1)) // 产生 [(1,2),(2,3),(3,4),(4,5)]
    .map(|(a, b)| a * b)         // 产生 [2, 6, 12, 20]
    .filter(|x| x % 3 == 0)      // 产生 [6, 12]
    .sum(); // 18
assert_eq!(18, sum);

迭代器的性能

迭代器是 Rust 零成本抽象(zero-cost abstraction)之一: 使用高级迭代器不会引入运行时开销。编译器通过循环展开、向量化、边界检查消除等手段,使迭代器与手写 for 循环的效率相当,甚至更好:

test bench_for  ... bench: 998,331 ns/iter (+/- 36,250) // 手写 for 循环
test bench_iter ... bench: 983,858 ns/iter (+/- 44,673) // 迭代器版本

在 Rust 中,迭代器来自函数式编程范式,让代码意图更清晰、更易组合,同时不牺牲性能。在需要遍历集合时,优先选择迭代器而非手动索引循环。

基于 MIT 协议发布