Rust中的Iterator和IntoIterator介绍及应用

这篇具有很好参考价值的文章主要介绍了Rust中的Iterator和IntoIterator介绍及应用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Iterator即迭代器,它可以用于对数据结构进行迭代。被迭代的数据结构是可迭代的(iterable),所谓的可迭代就是这个数据结构有返回迭代器的方法,由于Rust的所有权机制,对一个数据结构的迭代器,有三种:

  1. 拿走数据结构的所有权的迭代器,在数据结构的方法上体现为into_iter(self)。在Rust中,into方法通常都是拿走所有权的,而且方法的入参self也表明了会拿走所有权,不会拿走所有权的是&self、&mut self这种入参。
  2. 不拿走数据结构的所有权,只读取数据结构内容的迭代器,在数据结构的方法上体现为iter(&self)。&self表明了只是只读借用。
  3. 不拿走数据结构的所有权,但是可以读写数据结构内容的迭代器,在数据结构的方法上体现为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);
    }
}

运行的控制台输出:文章来源地址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模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • rust入门系列之Rust介绍及开发环境搭建

    Rust基本介绍 网站: https://www.rust-lang.org/ rust是什么 开发rust语言的初衷是: 在软件发展速度跟不上硬件发展速度,无法在语言层面充分的利用硬件多核cpu不断提升的性能和 在系统界别软件开发上,C++出生比较早,内存管理容易出现安全问题的背景下。 为了解决开发系统界别软

    2024年02月12日
    浏览(65)
  • Rust 笔记:Rust 语言中的字符串

    Rust 笔记 Rust 语言中的字符串 作者 : 李俊才 (jcLee95):https://blog.csdn.net/qq_28550263?spm=1001.2101.3001.5343 邮箱 : 291148484@163.com 本文地址 :https://blog.csdn.net/qq_28550263/article/details/130876665 【介绍】:本文介绍 Rust 语言中的字符和字符串的用法。 上一节:《 Rust 语言中使用 vector(向

    2024年02月06日
    浏览(53)
  • Rust 笔记:Rust 语言中的常量与变量

    Rust 笔记 Rust 语言中的常量与变量 作者 : 李俊才 (jcLee95):https://blog.csdn.net/qq_28550263?spm=1001.2101.3001.5343 邮箱 : 291148484@163.com 本文地址 :https://blog.csdn.net/qq_28550263/article/details/130875912 【介绍】:本文介绍 Rust 语言中的常量与变量。 上一节:《 上一节标题 》 | 下一节:《

    2024年02月06日
    浏览(60)
  • 【Rust 基础篇】Rust 树形结构:实现与应用

    树形结构是计算机科学中一种常见的数据结构,它具有层级结构和递归特性。在 Rust 中,我们可以使用结构体和枚举等语言特性来定义树形结构,并通过引用和所有权等机制有效地管理数据。本篇博客将详细介绍 Rust 中树形结构的实现和应用,并包含代码示例和对定义的详细

    2024年02月16日
    浏览(40)
  • 【Rust 基础篇】Rust 中的泛型:结构体和方法

    在 Rust 中,泛型是一种强大的特性,可以在结构体和方法中使用通用的类型参数。通过泛型,我们可以编写更加灵活和可复用的代码。本篇博客将详细介绍如何在 Rust 的结构体和方法中使用泛型,包括泛型结构体的定义、泛型方法的实现以及对泛型参数的约束。 在 Rust 中,我

    2024年02月16日
    浏览(42)
  • Rust 语言介绍及安装

    目录 1、简介 1.1 为什么选择Rust 高性能 可靠性 生产力 1.2 用 Rust 构建应用 命令行 WebAssembly 网络 嵌入式 2、安装 Rust Windows 的 Linux 子系统(WSL) 检查Rust 是最新的 卸载Rust版本: Cargo:Rust 的构建工具和包管理器 3、配置PATH环境变量 4、开发工具对Rust的支持。 VSCode 主要功能有

    2024年02月06日
    浏览(62)
  • rust语言介绍篇

    Rust出现就是为了解决C++面临的所有问题。Rust是一门系统编程语言 [1],专注于安全 [2],尤其是并发安全,支持函数式和命令式以及泛型等编程范式的多范式语言。Rust在语法上和C++类似 [3],设计者想要在保证性能的同时提供更好的内存安全。 Rust最初是由Mozilla研究院的Gra

    2024年02月01日
    浏览(36)
  • Rust介绍与开发环境搭建

    安装rust rust 安装官方指南:[HTPS][3W].rust-lang.org/tools/install (自己替换 HTPS,3W) Linux或者Macbook上安装rust 打开终端并输入下面命令: #因审核问题下面链接需要替换一下 HTPS-httpscurl  --tlsv1.2 [HTPS]://sh.rustup.rs -sSf | sh 这个命令将下载一个脚本并开始安装  rustup  工具,此工具

    2024年02月22日
    浏览(51)
  • 【Rust学习】安装Rust环境

    本笔记为了记录学习Rust过程,内容如有错误请大佬指教 使用IDE:vs code 参考教程:菜鸟教程链接: 菜鸟教程链接: 因为我已经安装过VSCode了,所以VSCode的安装方法在此处就不多介绍了,接下来就是安装Rust的编译工具。 Rust 编译工具 可以点击跳转下载Rust 编译工具 新建文件夹,

    2024年01月17日
    浏览(63)
  • Rust中的函数指针

    通过函数指针允许我们使用函数作为另一个函数的参数。函数的类型是 fn (使用小写的 ”f” )以免与 Fn 闭包 trait 相混淆。fn 被称为 函数指针(function pointer)。指定参数为函数指针的语法类似于闭包。 函数指针类型(使用  fn  写出)指向那些在编译时不必知道函

    2024年02月02日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包