跟着谷歌安卓团队学习Rust-强大的迭代器(Iterators)

158 阅读5分钟

跟着谷歌安卓团队学习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,你同样可以使用forwhile这些方式进行遍历的迭代器,不够一般不会这么做,都是使用函数组合进行操作,方便数据流的链式操作。

例如Vec、&Vec和&[T]都实现了它,所以你可以用for i in some_vec { .. }遍历向量。 但是,some_vec.next()这个方法是不存在的,因为IntoIterator才是真正提供迭代器功能的特质。如果有需要你要先执行 iter 让 Vec 变成迭代器后进行操作。

fn main() {
    let vec = vec![12345];
    // 创建向量迭代器
    let mut iter = vec.iter();
    // 遍历向量元素
    while let Some(val) = iter.next() {
        println!("{}", val);
    }
}

迭代器的组合

如果你是一个函数范式爱好者,那么你肯定使用过链式操作。如 map、filter、for_each、reduce 等,以便于创建更复杂的遍历逻辑。

Rust的迭代器同样支持这种的用法。举个例子:

fn main() {
    let vec = vec![12345];
    // 使用 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 = (u32u32);
    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 = (u32u32);

    fn next(&mut self-> Option<(u32u32)> {
        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![3579], y_coords: vec![10203040] };
    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<i32for 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.0vec![01234]);

// 也收集作品!

let iter = (0..5).into_iter();
let c: MyCollection = iter.collect();

assert_eq!(c.0vec![01234]);

FromIterator 特性定义了一个 from_iter 方法,该方法接受一个实现了 IntoIterator 特性的对象作为参数,并返回一个实现了 FromIterator 特性的集合。这个方法可以用于创建任何类型的集合,只要该类型实现了 FromIterator 特性。

文章持续更新,可以微信搜【梦兽编程】阅读,本文 rexweb.link 已收录,欢迎 Star 催更。

本文使用 markdown.com.cn 排版