Rust学习笔记
迭代器
迭代器是一个值,它可以生成一系列值
Iterrator 类型和 IntoIterator类型
- 迭代器是实现了Iterator类型的任意值
- IntoIterator是迭代器本身类型,Item是它生成的值的类型。
- 任意实现了IntoIterator的类型都可以成为可迭代者,可以通过into_iter获得一个迭代器
- 迭代器能生成值
- 迭代器生成的值是Item(条目)
- 接收迭代器条目的代码是消费者
- 大多数集合提供了iter和iter_mut方法,会返回该类型的迭代器,为每个条目生成共享引用或可变引用
-
给一个集合的共享引用,into_iter会返回一个条目的共享引用
-
给一个集合的可变引用,into_iter会返回一个条目的可变引用
-
当按值传递集合,into_iter会返回一个迭代器,获得集合的所有权,按值返回。这些值的所有权会传递给消费者,在迭代过程中消耗掉。
-
并非所有类型都提供了这3种实现,HashSet,BTreaSet,BinaryHeap不会在可变引用上实现IntoIterator,修改元素会破坏自身不变性规则-修改后的值可能有不同的hash。
from_fn 和 successors
from_fn:给定返回Option
successors:如果每个条目依赖前一个条目,std::iter::successors比较适合。提供一个初始条件和一个函数,且该函数能接受一个条目并返回下一个条目的Option。如果返回None则结束。
from_fn与successors都接受FnMut闭包
fn fibonacci() - > impl Iterator <Item=usize>{
let mut sate = (0,1);
std::iter::from_fn(move ||{
state = (state.1, state.0 + state.1);
Some(state.0)
})
}
assert_eq!(fibonacci().take(8).collect::<Vec<_>>(),
vec![1,1,2,3,5,8,13,21]);
from_fn successors 方法非常灵活,你可以将任何对迭代器的使用用它们来改写。
在使用这两个方法前,请确保你了解其他的迭代器不是最优的选择。
drain
许多集合提供了drain方法(抽取方法)。drain会接受一个对集合的可变引用,返回一个迭代器,将每个元素的所有权传给消费者。
与into_iter不同,drain只会借入对集合的可变引用,当迭代器丢弃时,drain会从集合中移除抽取范围的所有元素,无论drain是否会被消费者消耗。
map与filter
map是映射适配器,能针对迭代器的各个条目来调用闭包帮你转换迭代器。filter是过滤器,能使用闭包帮你从迭代器中过滤某些条目,由闭包决定保留和丢弃哪些条目。
map会按值传递每个条目给闭包,将闭包的结果的所有权转移给map的消费者。filter会共享引用每个条目给闭包,保留所有权,过滤后将所有权传递给filter的消费者。
要点1:单纯在迭代器上调用适配器不会消耗任何条目,只会返回一个新的迭代器。在适配器链中,实际完成任何工作的唯一方法是调用next。
要点2:迭代器的适配器是一种零成本抽象,由于map,filter和其他类似的适配器都是泛型,因此它们应用与迭代器就会专门针对涉及的特定类型生成特定代码。
filter_map和flat_map
filter_map与map类似,但它允许其闭包将条目转换为新条目(与map一样)或从迭代中丢弃该条目。闭包会返回一个option,当返回None时,该条目将从迭代中丢弃。
filter_map的作用在于,需要在闭包中尝试处理一下条目,看看是否ok,如果是None就丢弃,是ok的就保留。
等同于map(|| do_some()).filter(|| is_ok()).map(|| unwrap())
flat_map的功能是不仅仅像map一样返回一个条目,还可以返回任意数量的条目序列。flat_map的闭包必须返回一个可迭代者,比如一个Vec。
flatten
flatten适配器会串联迭代器的各个条目,这些条目应该都是可迭代者。例如在一个hashmap里保存vec序列作为值。调用flatten将会平铺整个结构。
flatten会返回一个迭代器,这个迭代器会生成一个一级条目的串联序列。
一个用法:如果只想从一个Vec<Option<...>>中迭代出Some值,可以这样用
vec![None,Some("day"),None,Some("one")].into_iter().flatten().collect:<Vec<_>>();
会返回
vec!["day","one"]
因为Option也实现了IntoIterator,表示有0个或1个元素组成的序列,None对迭代没有贡献,Some会贡献一个值。同样的,也可以用flatten迭代
Option<vec<...>>,None表示一个空向量。
有一些时候使用了map().flatten(),可以用flat_map来替代。
take与take_while
take与take_while是取出一定数目的条目,take_while可以决定终止的条件。
skip和skip_while
skip 和skip_while是跳过一定数目的条目,skip_while是可以决定停止跳过的条件。
peakable
peakable可以让我们先看看即将生成的下一个条目是啥,不需要消耗它。
peakable有一个额外的peek方法,会返回一个Option,如果迭代器耗尽则返回None。调用peek会尝试取下一个条目,如果存在将其缓存,直到调用next时给出。
fuse
fuse(保险丝)适配器能接受任何迭代器,并生成一个确保在第一次返回None后继续返回None的迭代器。
就是当一个迭代器发生了意外,这个意外可能会随着迭代器的迭代而溜掉,fuse可以让出现第一个None后,无论后续是不是Some,所有的迭代都返回None。
rev
有的迭代器可以从序列的两端抽取条目,使用rev可以反转序列迭代顺序。
inspect
inspect适配器是一种探针,为调试适配器流水线提供了便利。inspect接受闭包,但不会影响条目,可以打印一些信息。
chain
chain适配器可以将一个迭代器追加另一个迭代器后面。从第一个迭代器抽取条目,完成后继续从第二个迭代器抽取条目。
你可以将迭代器与任何会生成相同条目类型的可迭代者链接在一起。
enumerate
枚举适配器会将索引附加到序列中,它接受某个迭代器生成的条目a,b,c...,并返回一个新的条目序列(0,a),(1,b),(2,c)...
enumerate在HashMap中,获得是(key,value)。
zip
zip适配器会将两个迭代器组合成一个迭代器,新的迭代器会生成一对值,每个迭代器提供一个值组成一对,类似拉链拉合在一起。
by_ref
迭代器的by_ref方法会借入迭代器的可变引用,便于将各种适配器应用与该引用。这些适配器用完后,你重新可以获得原始迭代器的访问权。
避免适配器消耗迭代器的条目
cloned与copied
cloned适配器会接受一个生成引用的迭代器,并返回一个会生成从这些引用的克隆值的迭代器。将引用变成值。
copied比cloned更加严格,要求引用目标必须实现了copied
cycle
cycle适配器会返回一个迭代器,将无限循环生成序列。调用cycle的迭代器必须实现了clone,便于cycle保存其初始状态并在每次循环重新开始时调用它。
迭代器的消耗
累加
count 会从迭代器中取出条目,直到返回None,报告取出条目的数量
sum 和product 两个方法会分别计算迭代器条目的和和乘积
最小值与最大值
min 与 max 返回迭代器的最小和最大值 ,条目类型必须实现std::cmp::Ord,否则无法比较大小。
如果知道如何处理NaN值,就可以利用 max_by 和min_by,可以提供自己的比较函数。
min_by_key 和 max_by_key 可以按照键值来比较选择最小和最大值,它们接收一个闭包,闭包返回任意有序类型B,这就是需要比较的键值。
对条目序列比较
如果字符串,向量和切片的各个元素是可以比较的,可以使用< == 等运算符比较它们大小,不过运算符不能用作迭代器的比较,有个专门的消耗器
eq lt le gt ge 用于从迭代器中取出条目并比较。
any 和 all
any 从迭代器中查询任意让闭包中条件为true的条目,如果有这种条目,返回true,否则返回fale
all 是从迭代器中查询所有条目,可以让闭包中的条件为true,如果所有条目都满足条件,则返回true,否则返回false
position rposition ExactSizeIterator
position 会针对迭代器每个条目调用闭包,返回调用结果为true的第一个条目的索引的Option,否则返回None
rposition 是从右边开始查询每个条目。它要求使用可逆的迭代器,这样才能从右边开始查询。同时它也要求这个迭代器是ExactSizeIterator
大小要确定,以便能查询到索引值。
fold rfold
fold是常用的消耗器,用于在迭代器上生成序列累积的结果。需要给定fold一个初始值和闭包,fold会以当前累加值和下一个值为参数反复调用闭包。
最终累加值会返回,如果序列为空,只能返回初始值。
fold的闭包是两个参数,例如 fold(0,|n,i| n + i ) ,0是初始值,n是累加变量,i是下一个条目值。
rfold是从右边开始累积。
try_fold try_rfold
尝试折叠序列,有fold的不同是可以提前退出,不用消耗掉所有迭代器的值。try_fold的闭包的返回值会指出它应该是立即返回还是继续折叠。
闭包的返回类型有多种。
- 如果返回是Result<T,E>,返回Ok(v)则继续折叠,返回Err(e)则退出
- 如果返回是Option
,则返回Some(v)继续折叠,返回None则退出 - 闭包还可以返回一个std::ops::ControlFlow值,有两个变体,Continue(c)和Break(b),如果消耗了所有的迭代器的条目,则返回Continue(v)。如果中途返回,则返回Break(b)
使用ControlFlow类型的优点是,有时候中途退出不是一种错误。
fold总会消耗掉所有的迭代器的条目,很多场景都可以用try_fold来处理特殊情况。
nth与nth_back
nth会接受一个n作为参数,从迭代器中跳过n个条目,返回下一个。如果提前结束返回None
nth不会获得条目所有权
nth_back是获得倒数第n个条目
last
获得最后一个条目
find rfind 和 find_map
find会从迭代器中取出条目,返回让闭包中条件为true的条目。
rfind会从右边开始寻找。
find_map的闭包不会返回bool类型,返回某个值的Option
collect
collect用于构建迭代器条目的集合。collect本身不知道如何构建这些类型的集合,某些集合类型知道如何从迭代器中构建自己,会实现std::iter::FromIterator
collect只是便捷的封装。
extend
extend可以将一些可迭代者添加到集合中,扩展这个集合。
partition
将迭代器的条目划分到两个集合中,闭包来决定每个条目的位置,如果闭包判断条件为true,加入第一个集合,false则加入第二个集合
for_each try_for_each
for_each 会对每一个条目调用某个闭包,与for循环有点类似。
如果闭包需要容错或提前退出,可以使用try_for_each。
集合
Rust的8个标准集合
Vec
VecDeque
LinkList
BinaryHeap
HashMap<K,T> K:Eq+Hash 哈希表
BtreeMap<K,T> K:Ord 有序键值表
HashSet
BTreeSet
,
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话