13 - 迭代器
-
迭代器:是可以产生一系列值的值,通常用循环来操作。
-
示例:
/// 返回前n位正整数的和,即第n个三角形数 fn triangle(n: i32) -> i32 { let mut sum = 0; for i in 1..n+1 { sum += i; } sum } // 通过迭代器的flod方法实现上述函数 fn triangle(n: i32) -> i32 { (1..n+1).fold(0, |sum, item| sum + item) // fold 取得1..n+1产生的每个值,把累加值和这个值传给闭包。 // 然后闭包又返回新的累加值。 }
13.1-Iterator
和 IntoIterator
特型
-
Rust 迭代器:是任何实现 `std::iter::Iterator 特型的值。
trait Iterator { type Item; fn next(&mut self) -> Option<Self::Item>; ... // 许多默认方法 }
Item
是迭代器产生的值的类型。next
方法要么返回Some(v)
,其中v
是迭代器的下一个值;要么返回None
,表示序列终止。
-
IntoIterator
迭代器:实现迭代某种类型。trait IntoIterator where Self::IntoIter::Item == Self::Item { type Item; type IntoIter: Iterator; fn into_iter(self) -> Self::IntoIter; }
Item
是迭代器所产生值的类型。IntoIter
是迭代器值本身的类型。- 任何实现
IntoIterator
特型的类型,被称为可迭代类型(iterable)。
-
Rust 的循环可以实现迭代操作。如下所示迭代某个向量的元素:
println!("There's: "); let v = vec!["antimony", "arsenic", "aluminum", "selenium"]; for element in &v { // 迭代器 println!("{}", element); // 消费者 }
-
每个循环本质上只是对调用
IntoIterator
和Iterator
方法的简写:let mut iterator = (&v).into_iter(); while let Some(element) = iterator.next() { println!("{}", element); }
- 循环利用
IntoIterator::into_iter
方法将其操作数&v
转换为一个迭代器。 - 然后重复调用
Iterator::next
。 - 每次返回
Some(element)
,循环都执行其循环体。 - 如果返回的是
None
,则循环结束。
- 循环利用
-
迭代器产生的值叫迭代项(item)。
-
接收迭代器产生的迭代项的代码叫消费者(consumer)。
13.2 - 创建迭代器
13.2.1-iter
和 iter_mut
方法
-
大多数集合类型的
iter
和iter_mut
方法,返回该类型的迭代器,产生每个迭代项的共享或可修改引用。- 如下所示迭代器的迭代项类型是
&i32
- 每次调用
next
都会产生对下一个元素的引用,直到向量的末尾。
// 代替循环实现迭代器 let v = vec![4, 20, 12, 8, 6]; let mut iterator = v.iter(); assert_eq!(iterator.next(), Some(&4)); ...
- 如下所示迭代器的迭代项类型是
-
std::path::Path
的iter
方法返回的迭代器每次会产生路径的一个组件:- 这个迭代器的迭代项的类型是
&std::ffi::OsStr
; - 是操作系统调用可以接受的、借用的字符串切片。
use std::ffi::OsSter; use std::path::Path; let path = Path::new("C:/users/JimB/Downloads/Fedora.iso"); let mut iterator = path.iter(); assert_eq!(iterator.next(), Some(OsStr::new("C:"))); assert_eq!(iterator.next(), Some(OsStr::new("users"))); ...
- 这个迭代器的迭代项的类型是
13.2.2-IntoIterator
实现
-
如前所述,如果类型实现了
IntoIterator
,则可以调用它的into_iter
方法,实现等同循环的效果。use std::collections::BTreeSet; let mut favorites = BTreeSet::new(); favorites.insert("Lucy".to_string()); favorites.insert("Lie No. 3".to_string()); let mut it = favorites.into_iter(); assert_eq!(it.next(), Some("Lie No. 3".to_string())); assert_eq!(it.next(), Some("Lucy".to_string())); assert_eq!(it.next(), None);
-
大多数集合实现了多个
IntoIterator
,分别用于共享引用、可修改引用和转移。- 对于集合的共享引用:
into_iter
会返回产生迭代项共享引用的迭代器。 - 对于集合的可修改引用:
into_iter
会返回产生迭代项可修改引用的迭代器。 - 对于集合的按值转移:
into_iter
返回的迭代器会取得集合的所有权,并按值返回迭代器。此时,迭代器的所有权从结合转移到消费者,原始集合在这个过程中会被消费掉。
- 对于集合的共享引用:
-
for
循环会对其操作数应用IntoIterator::into_iter
,所以下述 3 个实现,可以支持迭代集合的共享引用、可修改引用,以及消费集合并取得其元素的所有权:- 每种实现,都对应上面的一种
IntoIterator
实现。
for element in &collection { ... } for element in &mut collection { ... } for element in collection { ... }
- 每种实现,都对应上面的一种
-
HashSet
、BTreeSet
和BinaryHeap
没有对可修改引用实现IntoIterator
。 -
HashMap
和BTreeMap
会产生它们值的可修改引用,但只产生它们键的共享引用。 -
切片实现了
IntoIterator
的共享引用和可修改引用。 -
IntoIterator
的共享引用和可修改引用的实现,等价于在引用值上调用iter
或iter_mut
方法:IntoIterator
是for
循环底层的基础;- 在不使用
for
循环时,it.iter()
要比(&it).into_iter()
清晰。
-
在泛型代码中,
T: IntoIterator
绑定可以限制类型变量T
为可迭代的类型。-
T: IntoIterator<Item=U>
可以进一步要求迭代产生指定的类型U
。// 接收任何可迭代类型,将它们通过{:?}格式,将值打印出来 use std::fmt::Debug; fn dump<T, U>(t: T) where T: IntoIterator<Item=U>, U: Debug { for u in t { println!("{:?}", u); } }
-
不能使用
iter
和iter_mut
来实现这个泛型函数,因为它们不是任何特型的方法。
-
13.2.3-drain
方法
-
drain
方法:以一个集合的可修改引用为参数,返回一个能把每个元素所有权传给消费者的迭代器。- 只是借用对集合的引用。
- 在迭代器被清除后,它会清空集合中所有剩余的元素。
-
在可通过范围指定索引的类型,如
String
、向量和VecDeque
中,drain
方法接收要移除元素的范围,而不是排取(drain)整个序列:use std::iter::FromIterator; let mut outer = "Eearth".to_string(); let inner = String::from_iter(outer.drain(1..4)); assert_eq!(outer, "Eh"); assert_eq!(inner, "art");
-
如果确实要排取整个序列,那么可以使用全范围(
..
)作为参数。
13.2.4 - 其他迭代器源
类型或特型 | 表达式 | 说明 |
---|---|---|
std::ops::Range |
1..10 |
端点必须是可以迭代的整数类型。范围包含起始值,不包含终止值。 |
std::ops::RangeFrom |
1.. |
无限定迭代。起始值必须时整数。如果值超过类型限制,可能会诧异或溢出 |
Option<T> |
Some(10).iter() |
类似长度为 0(None )或 1(Some(v) )的向量 |
Result<T, E> |
Ok("blah").iter() |
类似 Option ,产生 Ok 值 |
Vec<T>, &[T] |
v.windows(16) |
从左到右产生给定长度的每个连续切片。窗口重叠 |
v.chunks(16) |
从左到右产生给定长度的非重叠连续切片 | |
v.chunks_mut(1024) |
类似 chunks ,但切片是可修改的 |
|
`v.split( | byte | |
v.split_mut(...) |
同上,但产生可修改切片 | |
v.rsplit(...) |
类似 split ,但从右到左产生切片 |
|
v.splitn(n, ...) |
类似 split ,但最多产生 n 个切片 |
|
String, &str |
s.bytes() |
产生 UTF-8 形式的字节 |
s.chars() |
产生 UTF-8 表示的字符 | |
s.split_whitespace() |
以空格分割字符串,产生非空格字符的切片 | |
s.lines() |
产生字符串行的切片 | |
s.split('/') |
以给定模式分割字符串,产生匹配之间内容的切片。模式可以是字符、字符串、闭包等 | |
s.matches(char::is_numeric) |
产生匹配给定模式的切片 | |
std::collections::HashMap std::collections::BTreeMap |
map.keys(), map.values() |
产生对映射键或值的共享引用 |
map.values_mut() |
产生对条目值的可修改引用 | |
std::collections::HashSet std::collections::BTreeSet |
set1.union(set2) |
产生对 set1 与 set2 合集元素的共享引用 |
set1.intersection(set2) |
产生对 set1 与 set2 交集元素的共享引用 |
|
std::sync::mpsc::Receiver |
recv.iter() |
产生另一个线程中对应 Sender 发送的值 |
std::io::Read |
stream.bytes() |
从 IO 流产生字节 |
stream.chars() |
将流作为 UTF-8 解析并产生字符 | |
std::io::BufRead |
bufstream.lines() |
将流作为 UTF-8 解析并产生 String 行 |
bufstream.split(0) |
以给定字节分割流,产生该字节间的 Vec<u8> 缓冲 |
|
std::fs::ReadDir |
std::fs::read_dir(path) |
产生目录项 |
std::net::TcpListener |
listener.incoming() |
产生到来的网络连接 |
Free functions |
std::iter::empty() |
立即返回 None |
std::iter::once(5) |
产生给定的值,然后结束 | |
std::iter::repeat("#9") |
一直产生给定的值 |
详见《Rust 程序设计》(吉姆 - 布兰迪、贾森 - 奥伦多夫著,李松峰译)第十五章
原文地址