跟着谷歌安卓团队学习Rust-强大的迭代器(Iterators)
大家好,我是梦兽编程。欢迎回来与梦兽编程一起刷Rust的系列。
这是由 Google 的 Android开发团队的分享Rust课程。本课程涵盖了 Rust 的方方面面,从基本语法到泛型和错误处理等高级主题。
该课程的最新版本可以在 google.github.io/comprehensi…
如果你喜欢看梦兽编程的版本可以订阅跟着谷歌安卓团队学Rust订阅最新内容,梦兽编程也期待大家关注我的个人网站。
加入梦兽编程微信群,公众号回复111即可加入交流群与梦兽进行交流。
迭代器
在之前的分享中,我们都知道Rust 是一种系统编程语言,它的设计重点是内存安全和性能。在编码中迭代器是一个非常重要的概念,它提供了一种简洁高效的方式来遍历集合。
Rust的迭代器强大就强大在编译阶段就知道你的操作是否超出集合异常! 今日让我们一起来学习Rust的迭代器吧
我们先看一个代码
struct Fibonacci {
curr: u32,
next: u32,
}
impl Iterator for Fibonacci {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
let new_next = self.curr + self.next;
self.curr = self.next;
self.next = new_next;
Some(self.curr)
}
}
fn main() {
let fib = Fibonacci { curr: 0, next: 1 };
for (i, n) in fib.enumerate().take(5) {
println!("fib({i}): {n}");
}
}
在这个例子中我们可以看到迭代器是 Rust 中一种抽象数据类型
(ADT),它提供了一种遍历集合的接口(Iterator)并且实现next
的方法。
也就是说Rust标准库中许多类型都实现了迭代器特质,你也可以自行实现它。只要你实现了Iterator
,你同样可以使用for
,while
这些方式进行遍历的迭代器,不够一般不会这么做,都是使用函数组合进行操作,方便数据流的链式操作。
例如Vec、&Vec和&[T]都实现了它,所以你可以用for i in some_vec { .. }遍历向量。 但是,some_vec.next()这个方法是不存在的,因为IntoIterator才是真正提供迭代器功能的特质。如果有需要你要先执行 iter 让 Vec 变成迭代器后进行操作。
fn main() {
let vec = vec![1, 2, 3, 4, 5];
// 创建向量迭代器
let mut iter = vec.iter();
// 遍历向量元素
while let Some(val) = iter.next() {
println!("{}", val);
}
}
迭代器的组合
如果你是一个函数范式爱好者,那么你肯定使用过链式操作。如 map、filter、for_each、reduce 等,以便于创建更复杂的遍历逻辑。
Rust的迭代器同样支持这种的用法。举个例子:
fn main() {
let vec = vec![1, 2, 3, 4, 5];
// 使用 map 函数将每个元素乘以 2
let mut new_vec = vec.iter().map(|x| *x * 2).collect::<Vec<_>>();
// 使用 for_each 函数打印新向量的元素
new_vec.iter().for_each(|x| println!("{}", x));
}
更多api可查阅(rustwiki.org/zh-CN/core/…
IntoIterator
我们刚刚有说过Vec它原本是不支持next的方法,我们可以通过iter把集合转换成迭代器。
如果现在我有有一个新的结构体也需要支持这种特效,我们要怎么做呢?
先上代码
struct Grid {
x_coords: Vec<u32>,
y_coords: Vec<u32>,
}
impl IntoIterator for Grid {
type Item = (u32, u32);
type IntoIter = GridIter;
fn into_iter(self) -> GridIter {
GridIter { grid: self, i: 0, j: 0 }
}
}
struct GridIter {
grid: Grid,
i: usize,
j: usize,
}
impl Iterator for GridIter {
type Item = (u32, u32);
fn next(&mut self) -> Option<(u32, u32)> {
if self.i >= self.grid.x_coords.len() {
self.i = 0;
self.j += 1;
if self.j >= self.grid.y_coords.len() {
return None;
}
}
let res = Some((self.grid.x_coords[self.i], self.grid.y_coords[self.j]));
self.i += 1;
res
}
}
fn main() {
let grid = Grid { x_coords: vec![3, 5, 7, 9], y_coords: vec![10, 20, 30, 40] };
for (x, y) in grid {
println!("point = {x}, {y}");
}
}
我们主要看Grid结构体的实现,它实现了IntoIterator并且实现了into_iter,巧妙的将本来不支持 iter 的 x_coords 属性和 y_coords 。在执行 into_iter() 后获取GridIter对象让它进行next的遍历操作。
在rust的约束中,你可以省去不必要的迭代器方法执行,除非你想分开两行写。
// 只要你实现了两个接口,rust知道这是迭代器,默认你想遍历这个结合
for (x, y) in grid {
println!("point = {x}, {y}");
}
FromIterator
在上面的例子中,我可以看到我们每次执行完迭代器,想让迭代器转换回数组集合时都做一个操作。
// 使用 map 函数将每个元素乘以 2
let mut new_vec = vec.iter().map(|x| *x * 2).collect::<Vec<_>>();
collect方法就是让迭代器转换回一个数组集合的操作,它实现了FromIterator接口。这个特性通常用于将迭代器的元素收集到一个集合中,比如向量(Vec)、哈希表(HashMap)等。
// 一个样本集合,这只是 Vec<T> 的包装
#[derive(Debug)]
struct MyCollection(Vec<i32>);
// 让我们给它一些方法,以便我们可以创建一个方法并向其中添加一些东西。
impl MyCollection {
fn new() -> MyCollection {
MyCollection(Vec::new())
}
fn add(&mut self, elem: i32) {
self.0.push(elem);
}
}
// 我们将实现 FromIterator
impl FromIterator<i32> for MyCollection {
fn from_iter<I: IntoIterator<Item=i32>>(iter: I) -> Self {
let mut c = MyCollection::new();
for i in iter {
c.add(i);
}
c
}
}
// 现在我们可以创建一个新的迭代器...
let iter = (0..5).into_iter();
// ... 并用它制作一个 MyCollection
let c = MyCollection::from_iter(iter);
assert_eq!(c.0, vec![0, 1, 2, 3, 4]);
// 也收集作品!
let iter = (0..5).into_iter();
let c: MyCollection = iter.collect();
assert_eq!(c.0, vec![0, 1, 2, 3, 4]);
FromIterator 特性定义了一个 from_iter 方法,该方法接受一个实现了 IntoIterator 特性的对象作为参数,并返回一个实现了 FromIterator 特性的集合。这个方法可以用于创建任何类型的集合,只要该类型实现了 FromIterator 特性。
文章持续更新,可以微信搜【梦兽编程】阅读,本文 rexweb.link 已收录,欢迎 Star 催更。
本文使用 markdown.com.cn 排版