Rust中的Iterator和IntoIterator介绍及应用小结

 更新时间:2023年07月25日 11:13:42   作者:pilaf1990  
Iterator即迭代器,它可以用于对数据结构进行迭代,被迭代的数据结构是可迭代的(iterable),所谓的可迭代就是这个数据结构有返回迭代器的方法,这篇文章主要介绍了Rust中的Iterator和IntoIterator介绍及应用,需要的朋友可以参考下

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);
    }
}

完整代码:

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);
    }
}

运行的控制台输出:

做个真的我
刀剑如梦
难念的经
任逍遥
做个真的我
刀剑如梦
难念的经
任逍遥
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介绍及应用的文章就介绍到这了,更多相关Rust中的Iterator和IntoIterator内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Rust Aya 框架编写 eBPF 程序

    Rust Aya 框架编写 eBPF 程序

    这篇文章主要介绍了Rust Aya 框架编写 eBPF 程序方法的相关资料,需要的朋友可以参考下
    2022-11-11
  • Rust处理命令行参数

    Rust处理命令行参数

    在Rust中,命令行参数是程序从命令行接收的输入,它们为程序提供了运行时配置和数据的灵活性,本文就来介绍一下Rust处理命令行参数,具有一定的参考价值,感兴趣的可以了解一下
    2024-03-03
  • Rust个人学习小结之Rust的循环

    Rust个人学习小结之Rust的循环

    这篇文章主要介绍了Rust个人学习小结之Rust的循环,今天主要了解了Rust语言的3种循环方法: loop、while、for,本文结合实例代码给大家介绍的非常详细,需要的朋友可以参考下
    2023-01-01
  • Rust中泛型的学习笔记

    Rust中泛型的学习笔记

    在Rust语言中,泛型是一种强大的工具,它允许我们编写可复用且灵活的代码,本文主要介绍了Rust中泛型的学习笔记,具有一定的参考价值,感兴趣的可以了解一下
    2024-03-03
  • rust 中生成与使用protobuf的方法

    rust 中生成与使用protobuf的方法

    这篇文章主要介绍了rust中protobuf生成与使用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-05-05
  • 探索 Rust 中实用的错误处理技巧

    探索 Rust 中实用的错误处理技巧

    探索Rust中实用的错误处理技巧!Rust是一门静态类型系统安全且高效的编程语言,但使用过程中难免会遇到各种错误,学会如何正确处理这些错误至关重要,本指南将为您提供一些实用的错误处理技巧,帮助您更好地编写健壮的代码,需要的朋友可以参考下
    2024-01-01
  • Rust中HashMap类型的使用详解

    Rust中HashMap类型的使用详解

    Rust中一种常见的集合类型是哈希映射,本文主要介绍了Rust中HashMap类型的使用详解,包含创建访问修改遍历等,具有一定的参考价值,感兴趣的可以了解一下
    2024-03-03
  • Rust语言实现图像编码转换

    Rust语言实现图像编码转换

    image-rs库是 Rust 社区中广泛使用的一个开源库,它提供了丰富的图像编解码功能,本文主要介绍了Rust语言实现图像编码转换,具有一定的参考价值,感兴趣的可以了解一下
    2024-05-05
  • rust 包模块组织结构详解

    rust 包模块组织结构详解

    RUST提供了一系列的功能来帮助我们管理代码,包括决定哪些细节是暴露的、哪些细节是私有的,以及不同的作用域的命名管理,这篇文章主要介绍了rust 包模块组织结构的相关知识,需要的朋友可以参考下
    2023-12-12
  • rust标准库std::env环境相关的常量

    rust标准库std::env环境相关的常量

    在本章节中, 我们探讨了Rust处理命令行参数的常见的两种方式和处理环境变量的两种常见方式, 抛开Rust的语法, 实际上在命令行参数的处理方式上, 与其它语言大同小异, 可能影响我们习惯的也就只剩下语法,本文介绍rust标准库std::env的相关知识,感兴趣的朋友一起看看吧
    2024-03-03

最新评论