Rust中的Iterator和IntoIterator介紹及應用小結(jié)
Iterator即迭代器,它可以用于對數(shù)據(jù)結(jié)構(gòu)進行迭代。被迭代的數(shù)據(jù)結(jié)構(gòu)是可迭代的(iterable),所謂的可迭代就是這個數(shù)據(jù)結(jié)構(gòu)有返回迭代器的方法,由于Rust的所有權機制,對一個數(shù)據(jù)結(jié)構(gòu)的迭代器,有三種:
- 拿走數(shù)據(jù)結(jié)構(gòu)的所有權的迭代器,在數(shù)據(jù)結(jié)構(gòu)的方法上體現(xiàn)為
into_iter(self)
。在Rust中,into方法通常都是拿走所有權的,而且方法的入?yún)elf也表明了會拿走所有權,不會拿走所有權的是&self、&mut self這種入?yún)ⅰ?/li> - 不拿走數(shù)據(jù)結(jié)構(gòu)的所有權,只讀取數(shù)據(jù)結(jié)構(gòu)內(nèi)容的迭代器,在數(shù)據(jù)結(jié)構(gòu)的方法上體現(xiàn)為
iter(&self)
。&self表明了只是只讀借用。 - 不拿走數(shù)據(jù)結(jié)構(gòu)的所有權,但是可以讀寫數(shù)據(jù)結(jié)構(gòu)內(nèi)容的迭代器,在數(shù)據(jù)結(jié)構(gòu)的方法上體現(xiàn)為
iter_mut(&mut self)
。
每調(diào)用一次數(shù)據(jù)結(jié)構(gòu)的迭代器方法就會返回一個迭代器(拿走數(shù)據(jù)結(jié)構(gòu)所有權的迭代器方法只能調(diào)用一次),迭代器迭代完了就不能繼續(xù)使用了。
迭代器在Rust中是一個trait:
pub trait Iterator { /// The type of the elements being iterated over. /// 迭代器迭代的元素類型 #[stable(feature = "rust1", since = "1.0.0")] type Item; /// Advances(向前移動) the iterator and returns the next value. /// /// Returns [`None`] when iteration is finished. Individual iterator /// implementations may choose to resume iteration, and so calling `next()` /// again may or may not eventually start returning [`Some(Item)`] again at some /// point. /// 迭代器取下一個元素的方法,如果沒有下一個元素,會返回None /// 請注意next方法的入?yún)⑹?amp;mut self,也就是入?yún)⑹强勺兊牡鳎瑸槭裁纯勺??因為迭代器?nèi)部通常都有 /// 記錄迭代進度的變量,比如數(shù)組下標這種,隨著迭代的進行,變量會自增,所以需要改變迭代器的狀態(tài),用可變借用 fn next(&mut self) -> Option<Self::Item>; /// 省略其它內(nèi)容 }
你可以為你的數(shù)據(jù)結(jié)構(gòu)實現(xiàn)Iterator trait,使得它可迭代。我們通常在for循環(huán)中使用迭代器。Rust標準庫中的集合基本上都提供了返回迭代器的方法。比如我們最常用的Vec:
fn main() { // 下面是只讀迭代器的使用 let students = vec!["張三".to_string(),"李四".to_string(),"韓老二".to_string()]; for student in &students{ println!("&students寫法:{}",student); } // Vec的iter()方法返回的是只讀迭代器 for student in students.iter(){ println!("iter()方法調(diào)用寫法:{}",student); } let mut ugly_girls = vec!["韓老二".to_string(),"葉慧三".to_string()]; // for循環(huán)中的&mut ugly_girls寫法等同于ugly_girls.iter_mut() for girl in &mut ugly_girls{ girl.push_str("--really "); } // iter_mut()方法返回的是讀寫迭代器,可以對被迭代的元素進行修改 for girl in ugly_girls.iter_mut(){ girl.push_str("ugly"); } println!("{:?}",ugly_girls); let ugly_boys = vec!["吳亦".to_string(),"肖障".to_string()]; // for ugly_boy in ugly_boys等同于for ugly_boy in ugly_boys.into_iter for ugly_boy in ugly_boys { println!("{}",ugly_boy); } // 這兒不能再訪問ugly_boys了,因為它的所有權在for循環(huán)的時候就被轉(zhuǎn)移到迭代器中了 }
執(zhí)行上述的代碼后輸出:
&students寫法:張三
&students寫法:李四
&students寫法:韓老二
iter()方法調(diào)用寫法:張三
iter()方法調(diào)用寫法:李四
iter()方法調(diào)用寫法:韓老二
["韓老二--really ugly", "葉慧三--really ugly"]
吳亦
肖障
好了,讓我們來自己搞一個數(shù)據(jù)結(jié)構(gòu),然后為它實現(xiàn)三個迭代器方法加深理解。
假設我們有一個struct:
#[derive(Debug)] pub struct SongList { // 歌單中歌曲列表 pub songs: Vec<String>, // 歌單創(chuàng)建時間 pub create_time: SystemTime, }
表示歌單的數(shù)據(jù)結(jié)構(gòu),如果我們想要在for循環(huán)中去迭代歌單內(nèi)容,我們當然可以直接通過SongList的songs字段進行迭代,但是這個是利用了Vec為我們實現(xiàn)好的迭代器,如果我們不暴露SongList內(nèi)部的結(jié)構(gòu),把SongList當成一個黑盒去遍歷,我們需要為它實現(xiàn)幾個迭代器方法,每個方法返回一個迭代器,從而可以在for循環(huán)中通過迭代器來迭代SongList。
我們需要三個自定義的struct:Iter、IterMut、IntoIter分別表示SongList的三種迭代器,然后需要SongList提供三個方法分別返回三種迭代器:
impl SongList { fn iter(&self) -> Iter { Iter::new(self) } fn iter_mut(&mut self) -> IterMut { IterMut::new(self) } fn into_iter(self)->IntoIter{ IntoIter::new(self) } }
實現(xiàn)只讀迭代器
// 只讀迭代器,會用到引用,所以struct要帶生命周期范型,這兒遵循Rust習慣,用'a表示生命周期參數(shù) pub struct Iter<'a> { // 迭代器本身不擁有被迭代的數(shù)據(jù),而是引用被迭代的數(shù)據(jù),所以需要定義引用,有人的地方就有江湖,同樣有引用的地方就有生命周期'a pub song_list: &'a SongList, // 記錄迭代進度的變量 pub index: usize, } impl<'a> Iter<'a> { // 傳入&SongList引用,就可以創(chuàng)建迭代器Iter fn new(song_list: &'a SongList) -> Iter { Iter { song_list, index: 0, } } } // 為Iter實現(xiàn)Iterator trait,從而讓它變成真正的迭代器 impl<'a> Iterator for Iter<'a> { type Item = &'a String; fn next(&mut self) -> Option<Self::Item> { if self.index < self.song_list.songs.len() { let result = Some(&self.song_list.songs[self.index]); self.index += 1; result } else { None } } } // 為了讓我們可以在for循環(huán)中通過&song_list來替代song_list.iter(),需要為&SongList實現(xiàn)IntoIterator // 參考https://doc.rust-lang.org/std/iter/index.html中提到的內(nèi)容 // If a collection type C provides iter(), it usually also implements IntoIterator for &C, // with an implementation that just calls iter(). Likewise, a collection C that provides iter_mut() // generally implements IntoIterator for &mut C by delegating to iter_mut(). This enables a convenient shorthand: // // let mut values = vec![41]; // for x in &mut values { // same as `values.iter_mut()` // *x += 1; // } // for x in &values { // same as `values.iter()` // assert_eq!(*x, 42); // } // assert_eq!(values.len(), 1); impl<'a> IntoIterator for &'a SongList { // 我們的SongList中被迭代的songs是Vec<String>,所以這兒迭代的Item就是&String type Item = &'a String; type IntoIter = Iter<'a>; fn into_iter(self) -> Self::IntoIter { self.iter() } } impl SongList { fn iter(&self) -> Iter { Iter::new(self) } } fn main() { let song_list = SongList { songs: vec![ "做個真的我".to_string(), "刀劍如夢".to_string(), "難念的經(jīng)".to_string(), "任逍遙".to_string(), ], create_time: SystemTime::now(), }; for song in song_list.iter() { println!("{}", song); } for song in &song_list { println!("{}", song); } println!("song_list:{:#?}", song_list); }
實現(xiàn)可修改迭代器
// 讀寫迭代器 pub struct IterMut<'a> { // 要持有被迭代數(shù)據(jù)結(jié)構(gòu)的可變應用 song_list: &'a mut SongList, index: usize, } impl<'a> IterMut<'a> { // 將&mut SongList變成IterMut的方法 fn new(song_list: &'a mut SongList) -> IterMut { IterMut { song_list, index: 0, } } } // 為IterMut實現(xiàn)Iterator trait,讓它成為一個真正的迭代器 impl<'a> Iterator for IterMut<'a> { type Item = &'a mut String; fn next(& mut self) -> Option<Self::Item> { if self.index < self.song_list.songs.len() { let ptr = self.song_list.songs.as_mut_ptr(); let result = Some(unsafe{&mut *ptr.add(self.index)}); // 上面兩行不能用下面這一行實現(xiàn), // 否則會報錯:error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements // 參考:https://stackoverflow.com/questions/62361624/lifetime-parameter-problem-in-custom-iterator-over-mutable-references // let result = Some(&mut self.song_list.songs[self.index]); self.index += 1; result } else { None } } } // 為了讓我們可以在for循環(huán)中通過&mut song_list來替代song_list.iter_mut(),需要為&mut SongList實現(xiàn)IntoIterator impl<'a> IntoIterator for &'a mut SongList { type Item = &'a mut String; type IntoIter = IterMut<'a>; fn into_iter(self) -> Self::IntoIter { self.iter_mut() } } impl SongList { fn iter_mut(&mut self) -> IterMut { IterMut::new(self) } } fn main() { let mut zhangsan_song_list = SongList { songs: vec!["笑臉盈盈".to_string(), "我是一只魚".to_string()], create_time: SystemTime::now(), }; for song in &mut zhangsan_song_list{ song.push_str("-真的"); } for song in zhangsan_song_list.iter_mut() { song.push_str("-好好聽啊"); } println!("zhagnsan_song_list:{:#?}", zhangsan_song_list); }
實現(xiàn)可以會拿走所有權的迭代器
// 定義一個會拿走所有權的迭代器struct pub struct IntoIter{ song_list:SongList // 不用index了,因為拿走所有權的話,從songs中拿走一個就少一個,不用再記錄迭代進度了,迭代過的都被從Vec中移除了 } impl IntoIter { // 將SongList直接消耗掉,變成IntoIter,傳入的song_list變量的所有權就這樣被轉(zhuǎn)移到了迭代器中 fn new(song_list:SongList)->Self{ IntoIter{ song_list } } } // 為IntoIter實現(xiàn)Iterator trait,讓它成為一個真正的迭代器 impl Iterator for IntoIter { type Item = String; fn next(&mut self) -> Option<Self::Item> { // 從Vec中pop出的元素就沒了,所以不需要我們額外定義index來記錄迭代進度了 self.song_list.songs.pop() } } // 為了讓我們可以在for循環(huán)中通過for item in song_list來替代for item in song_list.into_iter(),需要為SongList實現(xiàn)IntoIterator impl IntoIterator for SongList { type Item = String; type IntoIter = IntoIter; // 直接消耗掉SongList類型變量,將其所有權轉(zhuǎn)移到迭代器中 fn into_iter(self) -> Self::IntoIter { IntoIter::new(self) } } impl SongList { fn into_iter(self)->IntoIter{ IntoIter::new(self) } } fn main() { let lisi_song_list = SongList{ songs:vec!["天涯".to_string(),"死不了".to_string()], create_time:SystemTime::now() }; //或者直接寫成 for song in lisi_song_list{ for song in lisi_song_list.into_iter(){ println!("{}",song); } }
完整代碼:
use std::iter::Iterator; use std::time::SystemTime; #[derive(Debug)] pub struct SongList { // 歌單中歌曲列表 pub songs: Vec<String>, // 歌單創(chuàng)建時間 pub create_time: SystemTime, } // 只讀迭代器,會用到引用,所以struct要帶生命周期范型,這兒遵循Rust習慣,用'a表示生命周期參數(shù) pub struct Iter<'a> { // 迭代器本身不擁有被迭代的數(shù)據(jù),而是引用被迭代的數(shù)據(jù),所以需要定義引用,有人的地方就有江湖,同樣有引用的地方就有生命周期'a pub song_list: &'a SongList, // 記錄迭代進度的變量 pub index: usize, } impl<'a> Iter<'a> { // 傳入&SongList引用,就可以創(chuàng)建迭代器Iter fn new(song_list: &'a SongList) -> Iter { Iter { song_list, index: 0, } } } // 為Iter實現(xiàn)Iterator trait,從而讓它變成真正的迭代器 impl<'a> Iterator for Iter<'a> { type Item = &'a String; fn next(&mut self) -> Option<Self::Item> { if self.index < self.song_list.songs.len() { let result = Some(&self.song_list.songs[self.index]); self.index += 1; result } else { None } } } // 為了讓我們可以在for循環(huán)中通過&song_list來替代song_list.iter(),需要為&SongList實現(xiàn)IntoIterator // 參考https://doc.rust-lang.org/std/iter/index.html中提到的內(nèi)容 // If a collection type C provides iter(), it usually also implements IntoIterator for &C, // with an implementation that just calls iter(). Likewise, a collection C that provides iter_mut() // generally implements IntoIterator for &mut C by delegating to iter_mut(). This enables a convenient shorthand: // // let mut values = vec![41]; // for x in &mut values { // same as `values.iter_mut()` // *x += 1; // } // for x in &values { // same as `values.iter()` // assert_eq!(*x, 42); // } // assert_eq!(values.len(), 1); impl<'a> IntoIterator for &'a SongList { // 我們的SongList中被迭代的songs是Vec<String>,所以這兒迭代的Item就是&String type Item = &'a String; type IntoIter = Iter<'a>; fn into_iter(self) -> Self::IntoIter { self.iter() } } // 讀寫迭代器 pub struct IterMut<'a> { // 要持有被迭代數(shù)據(jù)結(jié)構(gòu)的可變應用 song_list: &'a mut SongList, index: usize, } impl<'a> IterMut<'a> { // 將&mut SongList變成IterMut的方法 fn new(song_list: &'a mut SongList) -> IterMut { IterMut { song_list, index: 0, } } } // 為IterMut實現(xiàn)Iterator trait,讓它成為一個真正的迭代器 impl<'a> Iterator for IterMut<'a> { type Item = &'a mut String; fn next(& mut self) -> Option<Self::Item> { if self.index < self.song_list.songs.len() { let ptr = self.song_list.songs.as_mut_ptr(); let result = Some(unsafe{&mut *ptr.add(self.index)}); // 上面兩行不能用下面這一行實現(xiàn), // 否則會報錯:error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements // 參考:https://stackoverflow.com/questions/62361624/lifetime-parameter-problem-in-custom-iterator-over-mutable-references // let result = Some(&mut self.song_list.songs[self.index]); self.index += 1; result } else { None } } } // 為了讓我們可以在for循環(huán)中通過&mut song_list來替代song_list.iter_mut(),需要為&mut SongList實現(xiàn)IntoIterator impl<'a> IntoIterator for &'a mut SongList { type Item = &'a mut String; type IntoIter = IterMut<'a>; fn into_iter(self) -> Self::IntoIter { self.iter_mut() } } // 定義一個會拿走所有權的迭代器struct pub struct IntoIter{ song_list:SongList // 不用index了,因為拿走所有權的話,從songs中拿走一個就少一個,不用再記錄迭代進度了,迭代過的都被從Vec中移除了 } impl IntoIter { // 將SongList直接消耗掉,變成IntoIter,傳入的song_list變量的所有權就這樣被轉(zhuǎn)移到了迭代器中 fn new(song_list:SongList)->Self{ IntoIter{ song_list } } } // 為IntoIter實現(xiàn)Iterator trait,讓它成為一個真正的迭代器 impl Iterator for IntoIter { type Item = String; fn next(&mut self) -> Option<Self::Item> { // 從Vec中pop出的元素就沒了,所以不需要我們額外定義index來記錄迭代進度了 self.song_list.songs.pop() } } // 為了讓我們可以在for循環(huán)中通過for item in song_list來替代for item in song_list.into_iter(),需要為SongList實現(xiàn)IntoIterator impl IntoIterator for SongList { type Item = String; type IntoIter = IntoIter; // 直接消耗掉SongList類型變量,將其所有權轉(zhuǎn)移到迭代器中 fn into_iter(self) -> Self::IntoIter { IntoIter::new(self) } } impl SongList { fn iter(&self) -> Iter { Iter::new(self) } fn iter_mut(&mut self) -> IterMut { IterMut::new(self) } fn into_iter(self)->IntoIter{ IntoIter::new(self) } } fn main() { let song_list = SongList { songs: vec![ "做個真的我".to_string(), "刀劍如夢".to_string(), "難念的經(jīng)".to_string(), "任逍遙".to_string(), ], create_time: SystemTime::now(), }; for song in song_list.iter() { println!("{}", song); } for song in &song_list { println!("{}", song); } println!("song_list:{:#?}", song_list); let mut zhangsan_song_list = SongList { songs: vec!["笑臉盈盈".to_string(), "我是一只魚".to_string()], create_time: SystemTime::now(), }; for song in &mut zhangsan_song_list{ song.push_str("-真的"); } for song in zhangsan_song_list.iter_mut() { song.push_str("-好好聽啊"); } println!("zhagnsan_song_list:{:#?}", zhangsan_song_list); let lisi_song_list = SongList{ songs:vec!["天涯".to_string(),"死不了".to_string()], create_time:SystemTime::now() }; //或者直接寫成 for song in lisi_song_list{ for song in lisi_song_list.into_iter(){ println!("{}",song); } }
運行的控制臺輸出:
做個真的我
刀劍如夢
難念的經(jīng)
任逍遙
做個真的我
刀劍如夢
難念的經(jīng)
任逍遙
song_list:SongList {
songs: [
"做個真的我",
"刀劍如夢",
"難念的經(jīng)",
"任逍遙",
],
create_time: SystemTime {
tv_sec: 1690101338,
tv_nsec: 120053000,
},
}
zhagnsan_song_list:SongList {
songs: [
"笑臉盈盈-真的-好好聽啊",
"我是一只魚-真的-好好聽啊",
],
create_time: SystemTime {
tv_sec: 1690101338,
tv_nsec: 120146000,
},
}
死不了
天涯
到此這篇關于Rust中的Iterator和IntoIterator介紹及應用的文章就介紹到這了,更多相關Rust中的Iterator和IntoIterator內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Rust Atomics and Locks并發(fā)基礎理解
這篇文章主要為大家介紹了Rust Atomics and Locks并發(fā)基礎理解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-02-02Rust 數(shù)據(jù)分析利器polars用法詳解
這篇文章主要介紹了Rust 數(shù)據(jù)分析利器polars用法詳解,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧2024-08-08Rust重載運算符之復數(shù)四則運算的實現(xiàn)
這篇文章主要為大家詳細介紹了Rust如何實現(xiàn)復數(shù)以及復數(shù)的四則運算,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下2023-08-08