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_iter
for 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> {
// 迭代器本身不拥有被迭代的数据,而是引用被迭代的数据,所以需要定义引用,有人的地方就有江湖,同样有引用的地方就有生命周期'a
pub song_list: &'a SongList,
// 记录迭代进度的变量
pub index: usize,
}
impl<'a> Iter<'a> {
// 传入&SongList引用,就可以创建迭代器Iter
fn 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就是&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(),
"难念的经".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);
}
}
完整代码:文章来源:https://www.toymoban.com/news/detail-602973.html
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> {
// 迭代器本身不拥有被迭代的数据,而是引用被迭代的数据,所以需要定义引用,有人的地方就有江湖,同样有引用的地方就有生命周期'a
pub song_list: &'a SongList,
// 记录迭代进度的变量
pub index: usize,
}
impl<'a> Iter<'a> {
// 传入&SongList引用,就可以创建迭代器Iter
fn 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就是&String
type 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);
}
}
运行的控制台输出:文章来源地址https://www.toymoban.com/news/detail-602973.html
做个真的我
刀剑如梦
难念的经
任逍遥
做个真的我
刀剑如梦
难念的经
任逍遥
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,
},
}
死不了
天涯
到了这里,关于Rust中的Iterator和IntoIterator介绍及应用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!