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

 更新时间:2023年07月25日 11:13:42   作者:pilaf1990  
Iterator即迭代器,它可以用于对数据结构进行迭代,被迭代的数据结构是可迭代的(iterable),所谓的可迭代就是这个数据结构有返回迭代器的方法,这篇文章主要介绍了Rust中的Iterator和IntoIterator介绍及应用,需要的朋友可以参考下
(福利推荐:【腾讯云】服务器最新限时优惠活动,云服务器1核2G仅99元/年、2核4G仅768元/3年,立即抢购>>>:9i0i.cn/qcloud

(福利推荐:你还在原价购买阿里云服务器?现在阿里云0.8折限时抢购活动来啦!4核8G企业云服务器仅2998元/3年,立即抢购>>>:9i0i.cn/aliyun

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可迭代类型迭代器正确创建自定义可迭代类型的方法

    Rust可迭代类型迭代器正确创建自定义可迭代类型的方法

    在 Rust 中, 如果一个类型实现了 Iterator, 那么它会被同时实现 IntoIterator, 具体逻辑是返回自身, 因为自身就是迭代器,这篇文章主要介绍了Rust可迭代类型迭代器正确创建自定义可迭代类型的方法,需要的朋友可以参考下
    2023-12-12
  • Rust字符串匹配Rabin-Karp算法详解

    Rust字符串匹配Rabin-Karp算法详解

    Rabin-Karp算法也可以叫 Karp-Rabin 算法,它是用来解决多模式串匹配问题的,它的实现方式有点与众不同,首先是计算两个字符串的哈希值,然后通过比较这两个哈希值的大小来判断是否出现匹配,本文详细介绍了字符串匹配Rabin-Karp算法,需要的朋友可以参考下
    2023-05-05
  • 在Rust中编写自定义Error的详细代码

    在Rust中编写自定义Error的详细代码

    Result<T, E> 类型可以方便地用于错误传导,Result<T, E>是模板类型,实例化后可以是各种类型,但 Rust 要求传导的 Result 中的 E 是相同类型的,所以我们需要编写自己的 Error 类型,本文给大家介绍了在Rust中编写自定义Error的详细代码,需要的朋友可以参考下
    2024-01-01
  • Rust实现一个表达式Parser小结

    Rust实现一个表达式Parser小结

    这篇文章主要为大家介绍了Rust实现一个表达式Parser小结,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • Rust开发环境搭建到运行第一个程序HelloRust的图文教程

    Rust开发环境搭建到运行第一个程序HelloRust的图文教程

    本文主要介绍了Rust开发环境搭建到运行第一个程序HelloRust的图文教程,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-12-12
  • rust 包模块组织结构详解

    rust 包模块组织结构详解

    RUST提供了一系列的功能来帮助我们管理代码,包括决定哪些细节是暴露的、哪些细节是私有的,以及不同的作用域的命名管理,这篇文章主要介绍了rust 包模块组织结构的相关知识,需要的朋友可以参考下
    2023-12-12
  • Rust字符串字面值的一些经验总结

    Rust字符串字面值的一些经验总结

    字符串有两种表现形式,一种是基本类型,表示字符串的切片,以&str表示,另一种是可变的string类型,下面这篇文章主要给大家介绍了关于Rust字符串字面值的相关资料,需要的朋友可以参考下
    2022-04-04
  • C和Java没那么香了,Serverless时代Rust即将称王?

    C和Java没那么香了,Serverless时代Rust即将称王?

    Serverless Computing,即”无服务器计算”,其实这一概念在刚刚提出的时候并没有获得太多的关注,直到2014年AWS Lambda这一里程碑式的产品出现。Serverless算是正式走进了云计算的舞台
    2021-06-06
  • Rust for循环语法糖背后的API场景分析

    Rust for循环语法糖背后的API场景分析

    for语句是一种能确定循环次数的循环,for 语句用于执行代码块指定的次数,今天通过本文给大家介绍Rust for循环语法糖背后的API场景分析,感兴趣的朋友跟随小编一起看看吧
    2022-11-11
  • 深入了解Rust的生命周期

    深入了解Rust的生命周期

    生命周期指的是引用保持有效的作用域,Rust?的每个引用都有自己的生命周期。本文将通过示例和大家详细说说Rust的生命周期,需要的可以参考一下
    2022-11-11

最新评论

?


http://www.vxiaotou.com