Rust入门之迭代器(Iterators)

Rust入门之迭代器(Iterators)

本文已同步本人博客网站
本文相关源码已上传Github

前言

迭代器(Iterators)是 Rust 中最核心的工具之一,它不仅是遍历集合的抽象,更是 Rust 零成本抽象(Zero-Cost Abstractions)和所有权系统完美结合的典范。与其他语言不同,Rust 的迭代器在提供高效遍历能力的同时,通过编译器的严格检查,确保内存安全和性能优化,从而避免了其他语言中常见的迭代器失效或越界访问等问题。本文将跟随《Rust 程序设计语言》通过剖析迭代器的设计原理、使用方法及底层机制,帮助你掌握这一工具,并写出更符合 Rust 哲学的代码。

定义

迭代器模式允许你依次对一个序列中的项执行某些操作。迭代器iterator)负责遍历序列中的每一项并确定序列何时结束的逻辑。

在 Rust 中,迭代器是 惰性的lazy),这意味着在调用消费迭代器的方法之前不会执行任何操作。

fn main() {let v1 = vec![1, 2, 3];// 创建了一个迭代器,这段代码本身并没有执行任何有用的操作。let v1_iter = v1.iter();
}
  • 惰性求值的优势:避免不必要的计算开销,例如处理无限序列或仅在需要时生成元素。
  • 实际应用场景:处理大型数据集时,惰性迭代器可以节省内存,只在需要时加载数据。
  • 显式消费的必要性:必须调用消费方法(如collect())才能触发实际迭代,否则不会执行任何操作。

使用for 关键字打印出数组中的元素。

fn main() {let v1 = vec![1, 2, 3];// 创建了一个迭代器,这段代码本身并没有执行任何有用的操作。let v1_iter = v1.iter();for val in v1_iter {println!("Got: {}", val);}
}

Iterator Trait 和 next方法

迭代器都实现了一个叫做 Iterator 的定义于标准库的 trait,源码如下:

pub trait Iterator {type Item;fn next(&mut self) -> Option<Self::Item>;// 此处省略了方法的默认实现
}

type ItemSelf::Item,它们定义了 trait 的 关联类型。意味着实现 Iterator trait的时候必须定义一个Item,用于next 函数的返回元素的类型。

next函数

nextIterator 实现者被要求定义的唯一方法:next 方法,该方法每次返回迭代器中的一个项,封装在 Some 中,并且当迭代完成时,返回 None

如果调用next函数,迭代器变量要声明为可变的。在迭代器上调用 next 方法会改变迭代器内部的状态,每次调用next函数都会消费迭代器。

#[cfg(test)]
mod tests {#[test]fn iterator_demonstration() {let v1 = vec![1, 2, 3];// 迭代器变量要声明为可变let mut v1_iter = v1.iter();assert_eq!(v1_iter.next(), Some(&1));assert_eq!(v1_iter.next(), Some(&2));assert_eq!(v1_iter.next(), Some(&3));assert_eq!(v1_iter.next(), None);}
}

代码中调用了四次next函数,如果有值返回的是由Some包着的引用,如果没有值了返回None。

根据需要可以获取不同引用所有权的迭代器:

  1. iter() 创建的是不可变引用的迭代器。
  2. into_iter() 能获得数组的的所有权,并返回具有所有权的值。
  3. iter_mut() 创建的是一个可以遍历到可变引用的迭代器。
方法元素类型所有权原集合后续可用性
iter()&T借用
iter_mut()&mut T可变借用
into_iter()T转移所有权

示例:所有权发生转移,println!不能再使用v1

let v1 = vec![1, 2, 3];
let v1_iter = v1.into_iter();
// println!("{:?}", v1); // 编译错误:value borrowed after move

消费迭代器的方法

Iterator trait 有一系列不同的由标准库提供默认实现的方法。我们可以在 Iterator trait 的标准库 API 文档中找到所有这些方法。一些方法在其定义中调用了 next 方法,这也就是为什么在实现 Iterator trait 时要求实现 next 方法的原因。这些调用 next 方法的方法被称为 消费适配器consuming adaptors),因为调用它们会消耗迭代器。一个消费适配器的例子是 sum 方法。这个方法获取迭代器的所有权并反复调用 next 来遍历迭代器,因而会消费迭代器。在遍历过程中,它将每个项累加到一个总和中,并在迭代完成时返回这个总和。

点开sum函数源码,并没看到调用next方法,这里其实是使用了 Sum traitIterator::sum() 的实现通过将遍历和累加委托给 Sum trait,只要能确认迭代器中存放的什么类型就能由对应的trait实现求和。后续这里我专门写一篇文章探究学习一下。

    #[stable(feature = "iter_arith", since = "1.11.0")]fn sum<S>(self) -> SwhereSelf: Sized,S: Sum<Self::Item>,{Sum::sum(self)}
  • 标准库sum方法文档:https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html#method.sum

使用sum() 对集合当中的元素求和

		#[test]fn test_sum() {let v1 = vec![1,2,3];let total: i32 = v1.iter().sum();assert_eq!(total, 6);}

创建迭代器的方法

Iterator trait 中定义了另一类方法,被称为 迭代适配器iterator adaptors),它们不会消耗当前的迭代器,而是通过改变原始迭代器的某些方面来生成不同的迭代器,如 map方法。

    #[test]fn test_map() {let v1 = vec![1,2,3];let v3: Vec<_> = v1.iter().map(|i| i + 1).collect();assert_eq!(v3, vec![2, 3, 4]);}

由于 map 接受一个闭包,因此我们可以指定希望在每个元素上执行的任何操作。这是一个很好的例子,展示了如何通过闭包来自定义某些行为,同时复用 Iterator trait 提供的迭代行为。

可以链式调用多个迭代器适配器来以一种可读的方式进行复杂的操作。不过因为所有的迭代器都是惰性的,你必须调用一个消费适配器方法,才能从这些迭代器适配器的调用中获取结果。

消费迭代器的方法叫做消费适配器

创建迭代器的方法 叫做迭代适配器

使用捕获其环境的闭包

很多迭代器适配器接受闭包作为参数,而我们通常会指定捕获其环境的闭包作为迭代器适配器的参数。

在这里再介绍一个迭代适配器filter(),也利用了闭包中的一个特性:通过闭包捕获定义它的环境中的值(跳转闭包相关文章)。

我们使用 filter 方法来获取一个闭包。该闭包从迭代器中获取一项并返回一个 bool。如果闭包返回 true,其值将会包含在 filter 提供的新迭代器中。如果闭包返回 false,其值不会被包含。

使用 filter 方法和一个捕获 shoe_size 的闭包,示例代码如下:

#[derive(PartialEq, Debug)]
struct Shoe {size:i32,style: String,
}fn shoes_in_size(shoes: Vec<Shoe>, shoe_size: i32) -> Vec<Shoe> {// 必须使用into_iter() 获得数组所有权,因为collect 要求生成一个Vec<Shoe>shoes.into_iter().filter(|s| s.size == shoe_size).collect()
}
#[test]fn filters_by_size() {let shoes = vec![Shoe {size: 10,style: String::from("sneaker"),},Shoe {size: 13,style: String::from("sandal"),},Shoe {size: 10,style: String::from("boot"),},];let in_my_size = shoes_in_size(shoes, 10);assert_eq!(in_my_size,vec![Shoe {size: 10,style: String::from("sneaker")},Shoe {size: 10,style: String::from("boot")},]);}

shoes_in_size 函数获取一个鞋子 vector 的所有权和一个鞋码作为参数。它返回一个只包含指定鞋码的鞋子的 vector。

shoes_in_size 函数体中调用了 into_iter 来创建一个获取 vector 所有权的迭代器。接着调用 filter 将这个迭代器适配成一个只含有那些闭包返回 true 的元素的新迭代器。

闭包从环境中捕获了 shoe_size 变量并使用其值与每一只鞋的大小作比较,只保留指定鞋码的鞋子。最终,调用 collect 将迭代器适配器返回的值收集进一个 vector 并返回。

这个测试展示当调用 shoes_in_size 时,返回的只会是与我们指定的鞋码相同的鞋子。


在代码中使用的是into_iter(),没有使用iter()是为什么呢? 这里再分析一下他们的区别:

iter()into_iter() 的核心区别

  • iter():生成一个 不可变引用 的迭代器,元素类型为 &T。原集合保留所有权,后续仍可使用。
  • into_iter():生成一个 拥有所有权 的迭代器,元素类型为 T。原集合被消耗(所有权转移),后续无法再使用。

调用collect()方法 试图将引用类型 &Shoe 收集到 Vec<Shoe> 中,但 Vec<Shoe> 只能存储 Shoe 类型的元素,不能存储引用,所以需要获得所有权。使用 into_iter() 是为了将元素的所有权从原集合转移到新集合中,确保 collect() 可以直接生成 Vec<Shoe>,避免类型不匹配和克隆开销。这是 Rust 所有权系统和类型安全性的直接体现。

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

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

相关文章

若依框架二次开发——RuoYi-AI 本地部署流程

文章目录 项目环境安装后端1. 下载项目2. 使用 IDEA 导入项目3. 配置 Maven4. 配置 Maven settings.xml5. 初始化数据库6. 启动 Redis7. 修改数据库配置8. 启动后端服务安装管理端1. 下载管理端项目2. 安装依赖3. 启动管理端4. 修改管理端配置安装用户端1. 下载用户端项目2. 安…

精品推荐-最新大模型MCP核心架构及最佳实践资料合集(18份).zip

精品推荐-最新大模型MCP核心架构及最佳实践资料合集&#xff0c;共18份。 1、2025年程序员必学技能&#xff1a;大模型MCP核心技术.pdf 2、MCP 架构设计剖析&#xff1a;从 Service Mesh 演进到 Agentic Mesh.pdf 3、MCP 架构设计深度剖析&#xff1a;使用 Spring AI MCP 四步…

DataWorks智能体Agent发布!基于MCP实现数据开发与治理自动化运行

在传统的数据开发工作中&#xff0c;企业用户或者开发者常常需要进行繁琐的配置、复杂的代码撰写、反复的性能调优和大量重复性的操作&#xff0c;才能实现数据开发、数据集成和数据治理等工作&#xff0c;效率十分低下。 近日&#xff0c;阿里云大数据开发治理平台DataWorks基…

IDEA 中右侧没有显示Maven

IDEA 中右侧没有显示Maven 1. 检查 Maven 项目是否正确加载 现象 • 项目是 Maven 项目&#xff0c;但右侧没有 Maven 工具窗口。 • 项目根目录下有 pom.xml&#xff0c;但 IDEA 没有识别为 Maven 项目。 解决方法 手动重新加载 Maven 项目&#xff1a; • 在 IDEA 中&…

罗技K860键盘

罗技蓝牙键盘的顶部功能键F1-F12的原本功能 单击罗技键盘的功能键时&#xff0c;默认响应的是键盘上面显示的快进、调节音量等功能。改变回F1~F12原本功能&#xff0c;同时按下 fn和esc组合键

什么是大型语言模型(LLM)?哪个大模型更好用?

什么是 LLM&#xff1f; ChatGPT 是一种大型语言模型 (LLM)&#xff0c;您可能对此并不陌生。它以非凡的能力而闻名&#xff0c;已证明能够出色地完成各种任务&#xff0c;例如通过考试、生成产品内容、解决问题&#xff0c;甚至在最少的输入提示下编写程序。 他们的实力现已…

css画右上角 角标三角形

.corner {position: absolute;top: -2rem;right: -2rem;width: 0;height: 0;border: 2rem solid red;border-bottom-color: transparent;border-top-color: transparent;border-left-color: transparent;transform: rotateZ(135deg); } 基本思路就是设置border&#xff0c;只设…

vue自定义颜色选择器

vue自定义颜色选择器 效果图&#xff1a; step0: 默认写法 调用系统自带的颜色选择器 <input type"color">step1:C:\Users\wangrusheng\PycharmProjects\untitled18\src\views\Home.vue <template><div class"container"><!-- 颜…

[Python] 企业内部应用接入钉钉登录,端内免登录+浏览器授权登录

[Python] 为企业网站应用接入钉钉鉴权&#xff0c;实现钉钉客户端内自动免登授权&#xff0c;浏览器中手动钉钉授权登录两种逻辑。 操作步骤 企业内部获得 开发者权限&#xff0c;没有的话先申请。 访问 钉钉开放平台-应用开发 创建一个 企业内部应用-钉钉应用。 打开应用…

[蓝桥杯 2023 国 Python A] 整数变换

P10985 [蓝桥杯 2023 国 Python A] 整数变换 题目背景 建议使用 PyPy3 提交本题。 题目描述 小蓝有一个整数 n n n。每分钟&#xff0c;小蓝的数都会发生变化&#xff0c;变为上一分钟的数 减去上一分钟的数的各个数位和。 例如&#xff0c;如果小蓝开始时的数为 23 23 …

【Linux】TCP_Wrappers+iptables实现堡垒机功能

规划 显示jumpserver的简单功能&#xff0c;大致的网络拓扑图如下 功能规划 & 拓扑结构 JumpServer&#xff08;堡垒机&#xff09;主要功能&#xff1a; 对访问目标服务器进行统一入口控制&#xff08;例如 nginx、mysql、redis&#xff09;。使用 iptables 做 NAT 转…

用HTML和CSS绘制佩奇:我不是佩奇

在这篇博客中&#xff0c;我将解析一个完全使用HTML和CSS绘制的佩奇(Pig)形象。这个项目展示了CSS的强大能力&#xff0c;仅用样式就能创造出复杂的图形&#xff0c;而不需要任何图片或JavaScript。 项目概述 这个名为"我不是佩奇"的项目是一个纯CSS绘制的卡通猪形象…

Spring 中 WebFlux 编写一个简单的 Controller

引言&#xff1a;响应式编程与 WebFlux 随着应用程序需要处理大量并发请求的情况越来越多&#xff0c;传统的 Servlet 编程模式可能无法满足高效和低延迟的需求。为了应对这种情况&#xff0c;Spring 5 引入了 WebFlux&#xff0c;一个基于响应式编程的 Web 框架&#xff0c;旨…

React十案例下

代码下载 登录模块 用户登录 页面结构 新建 Login 组件&#xff0c;对应结构: export default function Login() {return (<div className{styles.root}><NavHeader className{styles.header}>账号登录</NavHeader><form className{styles.form}>&…

100道C#高频经典面试题带解析答案——全面C#知识点总结

100道C#高频经典面试题带解析答案 以下是100道C#高频经典面试题及其详细解析&#xff0c;涵盖基础语法、面向对象编程、集合、异步编程、LINQ等多个方面&#xff0c;旨在帮助初学者和有经验的开发者全面准备C#相关面试。 &#x1f9d1; 博主简介&#xff1a;CSDN博客专家、CSD…

机动车号牌管理系统设计与实现(代码+数据库+LW)

摘 要 在如今社会上&#xff0c;关于信息上面的处理&#xff0c;没有任何一个企业或者个人会忽视&#xff0c;如何让信息急速传递&#xff0c;并且归档储存查询&#xff0c;采用之前的纸张记录模式已经不符合当前使用要求了。所以&#xff0c;对机动车号牌信息管理的提升&…

VMWare Workstation Pro17.6最新版虚拟机详细安装教程(附安装包教程)

目录 前言 一、VMWare虚拟机下载 二、VMWare虚拟机安装 三、运行虚拟机 前言 VMware 是全球领先的虚拟化技术与云计算解决方案提供商&#xff0c;通过软件模拟计算机硬件环境&#xff0c;允许用户在一台物理设备上运行多个独立的虚拟操作系统或应用。其核心技术可提升硬件…

DeepSeek的神经元革命:穿透搜索引擎算法的下一代内容基建

DeepSeek的神经元革命&#xff1a;穿透搜索引擎算法的下一代内容基建 ——从语义网络到价值共识的范式重构 一、搜索引擎的“内容饥渴症”与AI的基建使命 2024年Q1数据显示&#xff0c;百度索引网页总数突破3500亿&#xff0c;但用户点击集中在0.78%的高价值页面。这种“数据…

docker安装nginx,基础命令,目录结构,配置文件结构

Nginx简介 Nginx是一款轻量级的Web服务器(动静分离)/反向代理服务器及电子邮件&#xff08;IMAP/POP3&#xff09;代理服务器。其特点是占有内存少&#xff0c;并发能力强. &#x1f517;官网 docker安装Nginx &#x1f433; 一、前提条件 • 已安装 Docker&#xff08;dock…

Python Lambda表达式详解

Python Lambda表达式详解 1. Lambda是什么&#xff1f; Lambda是Python中用于创建匿名函数&#xff08;没有名字的函数&#xff09;的关键字&#xff0c;核心特点是简洁。它适用于需要临时定义简单函数的场景&#xff0c;或直接作为参数传递给高阶函数&#xff08;如map()、f…