跟着谷歌安卓团队学习Rust-强大的迭代器(Iterators)
大家好,我是梦兽编程。欢迎回来与梦兽编程一起刷Rust的系列。
这是由 Google 的 Android开发团队的分享Rust课程。本课程涵盖了 Rust 的方方面面,从基本语法到泛型和错误处理等高级主题。
该课程的最新版本可以在 google.github.io/comprehensi...
如果你喜欢看梦兽编程的版本可以订阅跟着谷歌安卓团队学Rust订阅最新内容,梦兽编程也期待大家关注我的个人网站。
加入梦兽编程微信群,公众号回复111即可加入交流群与梦兽进行交流。
迭代器
在之前的分享中,我们都知道Rust 是一种系统编程语言,它的设计重点是内存安全和性能。在编码中迭代器是一个非常重要的概念,它提供了一种简洁高效的方式来遍历集合。
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 变成迭代器后进行操作。
javascript
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的迭代器同样支持这种的用法。举个例子:
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/...%255Bhttps%3A%2F%2Frustwiki.org%2Fzh-CN%2Fcore%2Fiter%2Ftrait.Iterator.html%255D%25E6%259C%2589%25E5%25A4%259A%25E5%25B9%25B4%25E5%2585%25B6%25E4%25BB%2596%25E8%25AF%25AD%25E8%25A8%2580%25E5%25BC%2580%25E5%258F%2591%25E7%25BB%258F%25E9%25AA%258C%25E7%259C%258B%25E7%259D%2580%25E8%25BF%2599%25E4%25BA%259B%25E5%2587%25BD%25E6%2595%25B0%25E9%2583%25BD%25E6%2598%25AF%25E8%2580%2581%25E7%2586%259F%25E4%25BA%25BA%25E3%2580%2582 "https://rustwiki.org/zh-CN/core/iter/trait.Iterator.html)%5Bhttps://rustwiki.org/zh-CN/core/iter/trait.Iterator.html%5D%E6%9C%89%E5%A4%9A%E5%B9%B4%E5%85%B6%E4%BB%96%E8%AF%AD%E8%A8%80%E5%BC%80%E5%8F%91%E7%BB%8F%E9%AA%8C%E7%9C%8B%E7%9D%80%E8%BF%99%E4%BA%9B%E5%87%BD%E6%95%B0%E9%83%BD%E6%98%AF%E8%80%81%E7%86%9F%E4%BA%BA%E3%80%82")
IntoIterator
我们刚刚有说过Vec它原本是不支持next的方法,我们可以通过iter把集合转换成迭代器。
如果现在我有有一个新的结构体也需要支持这种特效,我们要怎么做呢?
先上代码
rust
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的约束中,你可以省去不必要的迭代器方法执行,除非你想分开两行写。
scss
// 只要你实现了两个接口,rust知道这是迭代器,默认你想遍历这个结合
for (x, y) in grid {
println!("point = {x}, {y}");
}
FromIterator
在上面的例子中,我可以看到我们每次执行完迭代器,想让迭代器转换回数组集合时都做一个操作。
rust
// 使用 map 函数将每个元素乘以 2
let mut new_vec = vec.iter().map(|x| *x * 2).collect::<Vec<_>>();
collect方法就是让迭代器转换回一个数组集合的操作,它实现了FromIterator接口。这个特性通常用于将迭代器的元素收集到一个集合中,比如向量(Vec)、哈希表(HashMap)等。
rust
// 一个样本集合,这只是 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 排版