🌀 一文看懂 Rust 迭代器
📚 目錄導航
- 什么是迭代器?為什么 Rust 到處都在用它?
- Rust 迭代器的底層邏輯是什么?
- 適配器 vs 消費者:誰是主角?
- 常見適配器:加工數據的全能工廠
- 常見消費者:迭代器的“吃貨家族”
- Rust 如何保證高性能?為什么懶執行這么厲害?
- ? 高階技巧通俗講透:by_ref、提前消費
- 🧠 小白也能寫的自定義迭代器:從邏輯到實戰
- 總結 + 常見場景整理
1?? 什么是迭代器?為什么 Rust 到處都在用它?
一句話:迭代器就是一個可以“一個個吐出值”的東西。
再通俗點:它像一個工人,負責“從集合里拿出元素”:
let v = vec![1, 2, 3];
let mut it = v.iter();println!("{:?}", it.next()); // Some(1)
println!("{:?}", it.next()); // Some(2)
println!("{:?}", it.next()); // Some(3)
println!("{:?}", it.next()); // None
每次調用 .next()
,它就給你一個 Option<T>
類型的值:
- 有值:
Some(x)
- 沒值了:
None
2?? Rust 迭代器的底層邏輯是什么?
Rust 用 trait Iterator
抽象了所有“可以被一項一項訪問”的數據結構:
pub trait Iterator {type Item;fn next(&mut self) -> Option<Self::Item>;
}
你只需要實現 next()
,它會一項一項地返回數據。每個適配器、消費者,都是圍繞 next()
構建的!
3?? 適配器 vs 消費者:誰才是主角?
對比 | 適配器(Adapter) | 消費者(Consumer) |
---|---|---|
功能 | 加工、變換、組合數據 | 真正“把數據用掉” |
是否懶執行 | ? 是 | ? 一調用就開始運行 |
是否返回迭代器 | ? 返回新的迭代器 | ? 返回值(Vec、i32、bool) |
示例 | .map() .filter() .take() | .collect() .sum() .find() |
類比思路 🧠:
適配器像工廠流水線,把原料(數據)加工好,
消費者像物流運輸,把結果打包送到你手里。
4?? 常見適配器:加工數據的全能工廠
名稱 | 用法 | 示例 | 通俗場景 |
---|---|---|---|
map() | 加工每個元素 | .map(|x| x + 1) | 全部加 1、轉大寫等 |
filter() | 篩選元素 | .filter(|x| *x > 3) | 只要大于 3 的 |
take(n) | 只取前 n 個 | .take(2) | 分頁、限流 |
skip(n) | 跳過前 n 個 | .skip(2) | 跳過表頭等 |
enumerate() | 帶索引 | .enumerate() | 打印編號 |
rev() | 倒序 | .rev() | 倒著打印 |
chain() | 拼接 | a.chain(b) | 合并多個集合 |
🌰 示例:
let result: Vec<_> = (1..=5).filter(|x| x % 2 == 1).map(|x| x * 10).collect();println!("{:?}", result); // [10, 30, 50]
5?? 常見消費者:迭代器的“吃貨家族”
名稱 | 用法 | 示例 | 作用 |
---|---|---|---|
collect() | 收集為 Vec、HashMap 等 | .collect::<Vec<_>>() | 最常用 |
for_each() | 遍歷做事 | .for_each(|x| println!("{}", x)) | 替代 for 循環 |
sum() | 求和 | .sum::<i32>() | 累加數字 |
product() | 求積 | .product::<i32>() | 全部相乘 |
count() | 個數 | .count() | 數據量統計 |
find() | 找一個滿足條件的值 | .find(|x| *x == 3) | 搜索場景 |
any() | 有一個滿足條件? | .any(|x| x > 5) | 判斷存在性 |
all() | 全部滿足條件? | .all(|x| x > 0) | 校驗條件 |
6?? Rust 如何保證高性能?為什么懶執行這么厲害?
Rust 的迭代器用的是“懶加載”思想:
💤 什么叫懶?
不會一開始就執行所有 .map()
和 .filter()
,而是只有你用 collect()
等消費器,才“觸發”處理流程。
let iter = (1..=3).map(|x| {println!("處理 {}", x);x + 1
}); // 什么都不會打印let v: Vec<_> = iter.collect(); // 現在才開始打印
7?? ? 高階技巧通俗講透
? by_ref()
是啥?
你拿一個可變引用傳遞給某個適配器,就能保留迭代器狀態。
🌰 示例:我們只想用這個迭代器的一部分
let mut it = vec![1, 2, 3, 4, 5].iter();let a: Vec<_> = it.by_ref().take(2).collect(); // 拿前兩個
let b: Vec<_> = it.collect(); // 剩下的還可以繼續用println!("{:?} {:?}", a, b); // [1, 2] [3, 4, 5]
8?? 🧠 小白也能寫的自定義迭代器:從邏輯到實戰
你只要記住兩點:
? 一個迭代器必須:
- 有個結構體保存狀態(比如當前數)
- 實現
Iterator
trait,提供next()
方法
🌰 例子:自己寫一個“從 1 數到 5”的迭代器
struct CountToFive {count: usize,
}impl Iterator for CountToFive {type Item = usize;fn next(&mut self) -> Option<Self::Item> {if self.count < 5 {self.count += 1;Some(self.count)} else {None}}
}
? 用法:
fn main() {let counter = CountToFive { count: 0 };for n in counter {println!("{}", n); // 輸出 1 到 5}
}
📌 模板套路(記住就能寫):
struct MyIter { 狀態字段 }impl Iterator for MyIter {type Item = 返回的類型;fn next(&mut self) -> Option<Self::Item> {// 判斷是否結束 + 返回 Some(...) or None}
}
? 什么場景該自己寫?
- 你要從 1 到 100,每次跳 7(非普通序列)
- 你要包裝異步、分頁、網絡流
- 想把某個結構變成“可遍歷”的對象
9?? 總結:迭代器能做什么?為啥這么香?
優點 | 表現 |
---|---|
? 安全 | 沒有手動索引越界的煩惱 |
? 高性能 | 零成本抽象,懶執行,自動內聯 |
? 表達力強 | 寫起來非常流暢,讀起來像“數據管道” |
? 可組合 | map + filter + take 無限組合 |
? 可擴展 | 你可以實現自己的迭代器,復用邏輯 |
🚀 常見實戰場景:
- 遍歷 Vec、HashMap、文件行
- 過濾無效數據
- 求和 / 聚合統計
- 實現分頁 / 分批處理
- 管道式業務數據處理(流式處理)