Iterator即迭代器,它可以用于对数据结构进行迭代。被迭代的数据结构是可迭代的(iterable),所谓的可迭代就是这个数据结构有返回迭代器的方法,由于Rust的所有权机制,对一个数据结构的迭代器,有三种:
- 拿走数据结构的所有权的迭代器,在数据结构的方法上体现为
into_iter(self)
。在Rust中,into方法通常都是拿走所有权的,而且方法的入参self也表明了会拿走所有权,不会拿走所有权的是&self、&mut self这种入参。 - 不拿走数据结构的所有权,只读取数据结构内容的迭代器,在数据结构的方法上体现为
iter(&self)
。&self表明了只是只读借用。 - 不拿走数据结构的所有权,但是可以读写数据结构内容的迭代器,在数据结构的方法上体现为
iter_mut(&mut self)
。
每调用一次数据结构的迭代器方法就会返回一个迭代器(拿走数据结构所有权的迭代器方法只能调用一次),迭代器迭代完了就不能继续使用了。
迭代器在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方法的入参是&mut self,也就是入参是可变的迭代器,为什么可变?因为迭代器内部通常都有/// 记录迭代进度的变量,比如数组下标这种,随着迭代的进行,变量会自增,所以需要改变迭代器的状态,用可变借用fn next(&mut self) -> Option<Self::Item>;/// 省略其它内容
}
你可以为你的数据结构实现Iterator trait,使得它可迭代。我们通常在for循环中使用迭代器。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()方法调用写法:{}",student);}let mut ugly_girls = vec!["韩老二".to_string(),"叶慧三".to_string()];// for循环中的&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_iterfor ugly_boy in ugly_boys {println!("{}",ugly_boy);}// 这儿不能再访问ugly_boys了,因为它的所有权在for循环的时候就被转移到迭代器中了
}
执行上述的代码后输出:
&students写法:张三
&students写法:李四
&students写法:韩老二
iter()方法调用写法:张三
iter()方法调用写法:李四
iter()方法调用写法:韩老二
["韩老二--really ugly", "叶慧三--really ugly"]
吴亦
肖障
好了,让我们来自己搞一个数据结构,然后为它实现三个迭代器方法加深理解。
假设我们有一个struct:
#[derive(Debug)]
pub struct SongList {// 歌单中歌曲列表pub songs: Vec<String>,// 歌单创建时间pub create_time: SystemTime,
}
表示歌单的数据结构,如果我们想要在for循环中去迭代歌单内容,我们当然可以直接通过SongList的songs字段进行迭代,但是这个是利用了Vec为我们实现好的迭代器,如果我们不暴露SongList内部的结构,把SongList当成一个黑盒去遍历,我们需要为它实现几个迭代器方法,每个方法返回一个迭代器,从而可以在for循环中通过迭代器来迭代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)}
}
实现只读迭代器
// 只读迭代器,会用到引用,所以struct要带生命周期范型,这儿遵循Rust习惯,用'a表示生命周期参数
pub struct Iter<'a> {// 迭代器本身不拥有被迭代的数据,而是引用被迭代的数据,所以需要定义引用,有人的地方就有江湖,同样有引用的地方就有生命周期'apub song_list: &'a SongList,// 记录迭代进度的变量pub index: usize,
}impl<'a> Iter<'a> {// 传入&SongList引用,就可以创建迭代器Iterfn new(song_list: &'a SongList) -> Iter {Iter {song_list,index: 0,}}
}// 为Iter实现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循环中通过&song_list来替代song_list.iter(),需要为&SongList实现IntoIterator
// 参考https://doc.rust-lang.org/std/iter/index.html中提到的内容
// 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就是&Stringtype 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(),"难念的经".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);
}
实现可修改迭代器
// 读写迭代器
pub struct IterMut<'a> {// 要持有被迭代数据结构的可变应用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实现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)});// 上面两行不能用下面这一行实现,// 否则会报错: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循环中通过&mut song_list来替代song_list.iter_mut(),需要为&mut SongList实现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);
}
实现可以会拿走所有权的迭代器
// 定义一个会拿走所有权的迭代器struct
pub struct IntoIter{song_list:SongList// 不用index了,因为拿走所有权的话,从songs中拿走一个就少一个,不用再记录迭代进度了,迭代过的都被从Vec中移除了
}impl IntoIter {// 将SongList直接消耗掉,变成IntoIter,传入的song_list变量的所有权就这样被转移到了迭代器中fn new(song_list:SongList)->Self{IntoIter{song_list}}
}// 为IntoIter实现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循环中通过for item in song_list来替代for item in song_list.into_iter(),需要为SongList实现IntoIterator
impl IntoIterator for SongList {type Item = String;type IntoIter = IntoIter;// 直接消耗掉SongList类型变量,将其所有权转移到迭代器中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>,// 歌单创建时间pub create_time: SystemTime,
}// 只读迭代器,会用到引用,所以struct要带生命周期范型,这儿遵循Rust习惯,用'a表示生命周期参数
pub struct Iter<'a> {// 迭代器本身不拥有被迭代的数据,而是引用被迭代的数据,所以需要定义引用,有人的地方就有江湖,同样有引用的地方就有生命周期'apub song_list: &'a SongList,// 记录迭代进度的变量pub index: usize,
}impl<'a> Iter<'a> {// 传入&SongList引用,就可以创建迭代器Iterfn new(song_list: &'a SongList) -> Iter {Iter {song_list,index: 0,}}
}// 为Iter实现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循环中通过&song_list来替代song_list.iter(),需要为&SongList实现IntoIterator
// 参考https://doc.rust-lang.org/std/iter/index.html中提到的内容
// 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就是&Stringtype Item = &'a String;type IntoIter = Iter<'a>;fn into_iter(self) -> Self::IntoIter {self.iter()}
}// 读写迭代器
pub struct IterMut<'a> {// 要持有被迭代数据结构的可变应用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实现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)});// 上面两行不能用下面这一行实现,// 否则会报错: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循环中通过&mut song_list来替代song_list.iter_mut(),需要为&mut SongList实现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变量的所有权就这样被转移到了迭代器中fn new(song_list:SongList)->Self{IntoIter{song_list}}
}// 为IntoIter实现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循环中通过for item in song_list来替代for item in song_list.into_iter(),需要为SongList实现IntoIterator
impl IntoIterator for SongList {type Item = String;type IntoIter = IntoIter;// 直接消耗掉SongList类型变量,将其所有权转移到迭代器中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(),"难念的经".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);}
}
运行的控制台输出:
做个真的我
刀剑如梦
难念的经
任逍遥
做个真的我
刀剑如梦
难念的经
任逍遥
song_list:SongList {songs: ["做个真的我","刀剑如梦","难念的经","任逍遥",],create_time: SystemTime {tv_sec: 1690101338,tv_nsec: 120053000,},
}
zhagnsan_song_list:SongList {songs: ["笑脸盈盈-真的-好好听啊","我是一只鱼-真的-好好听啊",],create_time: SystemTime {tv_sec: 1690101338,tv_nsec: 120146000,},
}
死不了
天涯