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_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,},
}
死不了
天涯

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/8971.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL】之复合查询

【MySQL】之复合查询 基本查询多表查询笛卡尔积自连接子查询单行子查询多行子查询多列子查询在from子句中使用子查询 合并查询小练习 基本查询 查询工资高于500或岗位为MANAGER的雇员&#xff0c;同时还要满足他们的姓名首字母为大写的J按照部门号升序而雇员的工资降序排序使用…

性能测试Ⅱ(压力测试与负载测试详解)

协议 性能理论&#xff1a;并发编程 &#xff0c;系统调度&#xff0c;调度算法 监控 压力测试与负载测试的区别是什么&#xff1f; 负载测试 在被测系统上持续不断的增加压力&#xff0c;直到性能指标(响应时间等)超过预定指标或者某种资源(CPU&内存)使用已达到饱和状…

全志F1C200S嵌入式驱动开发(解决spi加载过慢的问题)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 之前的几个章节当中,我们陆续解决了spi-nor驱动的问题、uboot支持spi-nor的问题。按道理来说,下面要做的应该就是用uboot的loady命令把kernel、dtb、rootfs这些文件下载到ddr,然…

Mysql中日期时间的加减

Mysql中日期时间的加减&#xff0c;可以使用date_add()函数和date_sub()函数 1、date_add()为日期增加一个时间间隔 语法格式为: DATE_ADD(date,INTERVAL expr type) #date 指定的时间&#xff0c;可以是now()&#xff0c;也可以是其它时间 #INTERVAL 间隔关键字 #expr 添加…

WebRTC Simulcast介绍

原文地址&#x1f447; https://blog.livekit.io/an-introduction-to-webrtc-simulcast-6c5f1f6402eb/ 你想知道的关于Simulcast的一切 Simulcast是WebRTC中最酷的功能之一,它允许WebRTC会议在参与者网络连接不可预测的情况下进行扩展。在这篇文章中,我们将深入探讨Simulcas…

element ui input 深层循环v-model绑定默认数据删除不了的情况

例子&#xff1a; 在项目开发中遇到的&#xff0c;简单记录一下 <el-input style"width: 180px"v-model"item.dataForm"input"handleChangeDataForm($event)"type"number"placeholder"请输入1-2的数值"size"smal…

欧姆龙CX系列PLC串口转以太网欧姆龙cp1hplc以太网连接电脑

你是否还在为工厂设备信息采集困难而烦恼&#xff1f;捷米特JM-ETH-CX转以太网通讯处理器为你解决这个问题&#xff01; 捷米特JM-ETH-CX转以太网通讯处理器专门为满足工厂设备信息化需求而设计&#xff0c;可以用于欧姆龙多个系列PLC的太网数据采集&#xff0c;非常方便构建生…

【育儿】幼儿学习笔记 (一)

文章目录 [TOC] 一、前言二、2~6岁 目标三、1~3年级 目标学习内容校内校外 三、学习技巧表达结构表达方法板块化学习学习进度控制古诗词推荐 四、听文史类幼儿园小学 五、看 一、前言 语文、英语属于语言类学科 二、2~6岁 目标 听 ★★★★★ \color{Orange}{★★★★★} ★…

设计模式之责任链模式

// 定义请求类 class Request {private String content;public Request(String content) {this.content content;}public String getContent() {return content;} }// 抽象处理器 abstract class Handler {protected Handler successor; // 后继处理器public void setSuccesso…

Go语言channel

通道 通道&#xff08;channel&#xff09;是Go语言提供的一种在gorountine之间进行数据创术的通信机制。通道的声明非常简单&#xff0c;只需要使用chan 关键字即可&#xff0c;关闭则需要使用close函数。 注意&#xff1a;通过close函数关闭channel不是必须的。不主动关闭的…

java中list集合根据枚举类型排序

自定义枚举类 import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import java.util.Arrays; Getter NoArgsConstructor AllArgsConstructor public enum ProContant {QCC("QCC",1),FQCC("FQCC",2),HELIHUA(&quo…

4、非线性数据结构

上一节课我们讲了线性数据结构&#xff0c;这一节我们说下非线性数据结构。 非线性数据结构&#xff0c;从字面意思来看&#xff0c;就是指不是线性的结构。线性结构的特点是只有一个前驱和一个后继。 那么非线性结构的特点就是有多个前驱或后继了。 如果只存在一个没有前驱的…

ChatGPT是什么?ChatGPT里的G、P、T分别指什么

前言 ChatGPT是一种基于人工智能技术的聊天机器人&#xff0c;它可以模拟人们的对话方式进行自然语言交流&#xff0c;并根据用户提出的问题、需求、意愿等信息提供相关服务或回答问题。 ChatGPT的G、P、T分别指“生成式”&#xff08;Generative&#xff09;、“预训练”&…

买卖股票的最佳时机系列

//方法一 class Solution { public:int dp[100005];int maxProfit(vector<int>& prices) {//dp[i]表示前i天买入卖出的获取的最大利润//min_val表示前i-1天买入的最小值&#xff1b;dp[0]0;int min_valprices[0];for(int i1;i<prices.size();i){dp[i]max(dp[i-1],…

HikariCP连接池

HikariCP连接池 HikariCP连接池是高性能的JDBC连接池&#xff0c;官网标注的三大特点&#xff1a;快速、简单、可靠&#xff0c;性能优于其他连接池。 官网详细地说明了HikariCP所做的一些优化&#xff0c;总结如下&#xff1a; 字节码精简&#xff1a;优化代码&#xff0c;直…

Jsonp劫持

JSONP 介绍 jsonp是一种协议&#xff0c;准确的说&#xff0c;他是json的一种使用模式&#xff0c;为了解决Json受同源策略限制的问题。 基本语法 JSONP的基本语法为&#xff1a;callback({“name”:”test”, “msg”:”success”}) 常见的例子包括函数调用&#xff08;如…

mac怎么转换音频格式?

mac怎么转换音频格式&#xff1f;相信很多小伙伴都知道&#xff0c;平时我们接触到的音频格式大多是mp3格式的&#xff0c;因为mp3是电脑上最为流行的音频格式&#xff0c;不过除了mp3格式外&#xff0c;还有很多不同的音频格式&#xff0c;有时候不同网上或者不同软件上下载到…

Java开发中的分层开发和整洁架构

分层开发(横向拆分) 分层开发的概念: maven多模块开发项目管理.可以利用这种管理功能,实现一个项目的多层次模块开发–分层开发. 比如,当前项目HelloController依赖HelloService 这样做目的: 复杂开发过程.解耦(不调整依赖关系,无法解耦).分层开发(横向拆分)和纵向拆分的区别…

麒麟v10-coredns 启动失败

现象 在麒麟ARM芯片的机器上搭建k8s&#xff0c;其中的的一个组件cordons 发现启动失败&#xff0c;查看日志如下所示&#xff1a;No such device or address 问题分析 期初猜测kubelet与containerd的cgroupDriver驱动不一致导致。分别查看是一致的。没有问题。发现系统存在…