rust 自定義迭代器的實現(xiàn)方法
1. 什么是迭代器???
簡單來說,迭代器就是一個“知道如何獲取下一個元素”的東西。
它是一種設(shè)計模式,允許你遍歷一個序列(比如數(shù)組、列表或你自定義的任何東西),而不需要關(guān)心序列內(nèi)部是怎么存儲的。
在 Rust 中,你最常見的迭代器用法就是 for 循環(huán):r` 循環(huán):
let numbers = vec![1, 2, 3];
// 這里的 `numbers.iter()` 就創(chuàng)建了一個迭代器
for num in numbers.iter() {
println!("Got: {}", num);
}
for 循環(huán)就是不斷地問這個迭代器:“嘿,還有下一個嗎?有的話請給我。” 直到迭代器回答:“抱歉,沒有了。”
我們的目標(biāo)就是學(xué)會如何創(chuàng)造這種“東西”。
2. 核心揭秘:IteratorTrait
在 Rust 中,“迭代器”并不是一個具體的類型,而是**任何了 Iterator Trait 的類型**。
這個 Trait (特質(zhì)) 的定義簡化后是這樣的:
pub trait Iterator {
// 1. 關(guān)聯(lián)類型:告訴 Rust 你迭代的“東西”是什么類型
type Item;
// 2. 核心方法:獲取下一個元素
// 這是你唯一必須實現(xiàn)的方法!
fn next(&mut self) -> Option<Self::Item>;
// --- 下面還有很多其他方法 (map, filter, sum...) ---
// 但它們都有默認(rèn)實現(xiàn),你暫時不用管!
}
是不是看起來很簡單?我們來拆解一下你必須關(guān)心的兩個部分:
type Item;
這是一個關(guān)聯(lián)類型。你只需要告訴 Rust:“我這個迭代器,每次‘吐’出來的元素是 i32 類型”或者“是 String 類型”。
例如:type Item = u32; 或 type Item = &String;
fn next(&mut self) -> Option<Self::Item>
這就是魔法發(fā)生的地方!??
&mut self:為什么是 &mut (可變借用)?因為迭代器需要**狀態(tài)**。比如,一個計數(shù)器需要知道“我當(dāng)前數(shù)到幾了”,每次調(diào)用 next 之后,這個狀態(tài)就要改變(比如 +1)。
Option<Self::Item>:這是迭代器設(shè)計的精髓!
- **`Some(ue)**:如果序列中還有下一個元素,就返回 Some(那個元素)`。
- None:如果序列已經(jīng)結(jié)束了,就返回 None。for 循環(huán)看到 None 就會自動停止。
3. 實踐一:你的第一個迭代器 (簡單的計數(shù)器) ??
我們來寫一個最簡單的迭代器:一個從 1 數(shù)到 5 的計數(shù)器。
第 1 步:定義結(jié)構(gòu)體 (保存狀態(tài))
迭代器需要“記憶”,所以我們需要一個結(jié)構(gòu)體來保存它的“狀態(tài)”。對于計數(shù)器,我們需要知道“當(dāng)前數(shù)到幾了” (current) 和“什么時候停” (max)。
// 我們的計數(shù)器結(jié)構(gòu)體
struct Counter {
current: u32,
max: u32,
}
// 順便給它一個 "構(gòu)造函數(shù)" (new)
impl Counter {
fn new(max: u32) -> Counter {
Counter { current: 1, max } // 我們從 1 開始數(shù)
}
}
第 2 步:實現(xiàn)IteratorTrait
現(xiàn)在,我們來告訴 Rust 如何讓 Counter 變成一個迭代器。
impl Iterator for Counter {
// 1. 告訴 Rust 我們迭代的是 u32
type Item = u32;
// 2. 實現(xiàn)核心邏輯!
fn next(&mut self) -> Option<Self::Item> {
if self.current <= self.max {
// 只要當(dāng)前值 <= 5
// 準(zhǔn)備好要返回的當(dāng)前值
let val_to_return = self.current;
// 更新狀態(tài):讓 current + 1,為下一次做準(zhǔn)備
self.current += 1;
// 把值用 Some() 包裹起來返回
Some(val_to_return)
} else {
// 如果 current 已經(jīng) > max (比如到了 6)
// 迭代結(jié)束!返回 None
None
}
}
}
第 3 步:使用它!
恭喜你!你已經(jīng)寫好了一個完整的迭代器!?? 讓我們用用看:
fn main() {
let counter = Counter::new(5); // 創(chuàng)建一個 1 到 5 的計數(shù)器
// `for` 循環(huán)現(xiàn)在可以識別我們的 Counter 了!
println!("Running for loop:");
for number in counter {
println!("{}", number);
}
// 注意:`for` 循環(huán)會“消耗掉”迭代器。
// 如果想再用一次,需要重新創(chuàng)建:
let counter2 = Counter::new(3);
// 你也可以手動調(diào)用 next() 看看發(fā)生了什么
println!("\nManual next() calls:");
let mut counter3 = Counter::new(2); // 必須是 mut,因為 next() 需要 &mut self
println!("{:?}", counter3.next()); // Some(1)
println!("{:?}", counter3.next()); // Some(2)
println!("{:?}", counter3.next()); // None (迭代結(jié)束)
println!("{:?}", counter3.next()); // None (之后永遠(yuǎn)是 None)
}
輸出:
Running for loop: 1 2 3 4 5 Manual next() calls: Some(1) Some(2) None None
你已經(jīng)掌握了 80% 的精髓了!太棒了!??
4. 實踐二:讓自定義結(jié)構(gòu)體“可迭代” ??
在實踐一中,Counter 本身就是迭代器。但更常見的情況是:你有一個集合(比如 `Myook),你想**為它創(chuàng)建一個迭代器**(比如 BookPageIterator`)。
就像 Vec (集合) 和 VecIter (它的迭代器) 的關(guān)系一樣。
我們希望實現(xiàn)這樣的效果:
let my_list = MyList::new();
for item in &my_list { // 注意這里是 &my_list
// ...
}
要實現(xiàn)這個,我們需要兩個 Trait:Iterator (老朋友) 和 IntoIterator (新朋友)。
IntoIterator Trait 就像一個“轉(zhuǎn)換器”,它告訴 for 循環(huán):“嘿,我知道如何把我(&MyList)轉(zhuǎn)換成一個真正的迭代器!”
第 1 步:定義集合和它的迭代器結(jié)構(gòu)體
// 我們的集合
struct MyList {
items: Vec<String>,
}
impl MyList {
fn new() -> Self {
Self {
items: vec![
"Rust".to_string(),
"is".to_string(),
"Awesome".to_string(),
],
}
}
}
// ------------------------------------
// 專門為 MyList 服務(wù)的迭代器結(jié)構(gòu)體
// 它需要“借用” MyList 的數(shù)據(jù)
// 'a 是生命周期,表示它借用的數(shù)據(jù)至少和 'a 活得一樣久
struct MyListIter<'a> {
list: &'a MyList, // 持有對 MyList 的引用
index: usize, // 跟蹤迭代到第幾個了
}
第 2 步:為 `MyListIter 實現(xiàn)Iterator
這和 Counter 的例子幾乎一樣,只是現(xiàn)在我們是從 Vec 中取數(shù)據(jù)。
// 'a 也要在這里聲明
impl<'a> Iterator for MyListIter<'a> {
// 這一次,我們迭代的是對 String 的引用
type Item = &'a String;
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.list.items.len() {
// 還有元素
let item = &self.list.items[self.index];
self.index += 1;
Some(item)
} else {
// 沒元素了
None
}
}
}
第 3 步:關(guān)鍵!為MyList實現(xiàn)IntoIterator
這是連接 for 循環(huán)和 MyListIter 的“膠水”。我們希望 for item in &my_list 能工作,所以我們要為 &MyList 實現(xiàn) IntoIterator。
// 為 &MyList (對 MyList 的不可變引用) 實現(xiàn) IntoIterator
impl<'a> IntoIterator for &'a MyList {
// 迭代項還是 &String
type Item = &'a String;
// 告訴 for 循環(huán):你調(diào)用 into_iter() 時,
// 我會返回一個 MyListIter<'a> 實例
type IntoIter = MyListIter<'a>;
// `for` 循環(huán)會自動調(diào)用這個方法!
// 這里的 self 就是 &'a MyList
fn into_iter(self) -> Self::IntoIter {
// 創(chuàng)建我們剛才定義的迭代器實例
MyListIter {
list: self, // self 就是 &MyList
index: 0, // 從 0 開始
}
}
}
第 4 步:見證奇跡!
fn main() {
let my_list = MyList::new();
// 感謝 IntoIterator,這行代碼現(xiàn)在可以完美工作了!
// 1. `for` 循環(huán)看到 &my_list
// 2. 它調(diào)用 (&my_list).into_iter()
// 3. 我們的代碼返回了一個 MyListIter
// 4. `for` 循環(huán)不斷調(diào)用 MyListIter.next()
for item in &my_list {
println!("Item: {}", item);
}
}
輸出:
Item: Rust
Item: is
Item: Awesome
你做到了!這已經(jīng)是 Rust 中非常地道的迭代器實現(xiàn)方式了!??
5. 你免費獲得的“超能力” ??
最爽的部分來了!
當(dāng)你辛辛苦苦地實現(xiàn)了 Iterator Trait(哪怕只寫了 next() 方法),Rust 編譯器會免費贈送給你一大堆超級好用的“迭代器適配器” (Iterator Adapters)!
比如 .map(), .filter(), .zip(), .sum(), .collect()… 全都能用了!
看看我們剛才的 Counter:
let sum: u32 = Counter::new(5) // 我們的迭代器 (1, 2, 3, 4, 5)
.zip(Counter::new(5).skip(1)) // ( (1,2), (2,3), (3,4), (4,5) )
.map(|(a, b)| a * b) // ( 2, 6, 12, 20 )
.filter(|x| *x > 10) // ( 12, 20 )
.sum(); // 12 + 20 = 32
println!("The complex sum is: {}", sum); // 32
我們只寫了 next(),但 zip, skip, map, filter, sum 都能在我們的 Counter 上使用!這就是 Rust Trait 和迭代器模式的強大之處!
6. 總結(jié) & 下一步
我們來回顧一下關(guān)鍵點:
- 迭代器是任何實現(xiàn)了 Iterator Trait 的東西。
- Iterator Trait 的核心是 type Item; (迭代什么) 和 `fn next(&mut self) -> OptionSelf::Item 你需要一個 struct 來保存迭代的狀態(tài) (比如 current 索引)。
- `next)方法通過返回Some(value)來提供值,通過返回None` 來停止迭代。
- 要讓你自己的集合(如 MyList)支持 for item in &collection,你需要為 &collection 實現(xiàn) IntoIterator Trait,讓它返回你自定義的迭代器(如 MyListIter)。
- 一旦實現(xiàn)了 Iterator,你就免費獲得了所有適配器 (map, `filter …)。
到此這篇關(guān)于rust 自定義迭代器的實現(xiàn)方法的文章就介紹到這了,更多相關(guān)rust 自定義迭代器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Rust中的方法與關(guān)聯(lián)函數(shù)使用解讀
在Rust中,方法是定義在特定類型(如struct)的impl塊中,第一個參數(shù)是self(可變或不可變),方法用于描述該類型實例的行為,而關(guān)聯(lián)函數(shù)則不包含self參數(shù),常用于構(gòu)造新實例或提供一些與實例無關(guān)的功能,Rust的自動引用和解引用特性使得方法調(diào)用更加簡潔2025-02-02
Rust動態(tài)調(diào)用字符串定義的Rhai函數(shù)方式
Rust中使用Rhai動態(tài)調(diào)用字符串定義的函數(shù),通過eval_expression_with_scope實現(xiàn),但參數(shù)傳遞和函數(shù)名處理有局限性,使用FnCall功能更健壯,但更復(fù)雜,總結(jié)提供了更通用的方法,但需要處理更多錯誤情況2025-02-02
rust標(biāo)準(zhǔn)庫std::env環(huán)境相關(guān)的常量
在本章節(jié)中, 我們探討了Rust處理命令行參數(shù)的常見的兩種方式和處理環(huán)境變量的兩種常見方式, 拋開Rust的語法, 實際上在命令行參數(shù)的處理方式上, 與其它語言大同小異, 可能影響我們習(xí)慣的也就只剩下語法,本文介紹rust標(biāo)準(zhǔn)庫std::env的相關(guān)知識,感興趣的朋友一起看看吧2024-03-03

