rust学习-迭代器

迭代器的实现方式提供了对多种不同的序列使用相同逻辑的灵活性

没有迭代器时只能用索引,迭代器处理了所有这些逻辑,减少了重复代码,消除了潜在的混乱

示例

复制代码
fn main() {
    let v1 = vec![1, 2, 3];
    let v1_iter = v1.iter();

    // 无需使 v1_iter 可变,因为 for 循环会获取 v1_iter 的所有权并在后台使 v1_iter 可变
    for val in v1_iter {
        println!("Got: {}", val);
    }
}

原理

迭代器都实现了一个叫做 Iterator 的定义于标准库的 trait

复制代码
pub trait Iterator {
    // type Item 和 Self::Item 定义了 trait 的 关联类型,associated type
    // 实现 Iterator trait 要求同时定义一个 Item 类型
    // 这个 Item 类型被用作 next 方法的返回值类型
    type Item;

    // next 一次返回迭代器中的一个项,封装在 Some 中
    // 当迭代器结束时,它返回 None
    fn next(&mut self) -> Option<Self::Item>;

    // 此处省略了方法的默认实现
}

使用iter执行一个个的遍历

复制代码
#[test]
fn iterator_demonstration() {
    let v1 = vec![1, 2, 3];
    // v1_iter 可变
    // 在迭代器上调用 next 方法改变了迭代器中用来记录序列位置的状态
    // 从 next 调用中得到的值是 vector 的不可变引用
    let mut v1_iter = v1.iter();

    assert_eq!(v1_iter.next(), Some(&1));
    assert_eq!(v1_iter.next(), Some(&2));
    assert_eq!(v1_iter.next(), Some(&3));
    assert_eq!(v1_iter.next(), None);
}

iter:生成一个不可变引用的迭代器

into_iter:获取 v1 所有权并返回拥有所有权的迭代器

iter_mut:迭代可变引用

消费适配器

调用它们会消耗迭代器

比如sum:获取迭代器的所有权并反复调用 next 来遍历迭代器,因而会消费迭代器。当其遍历每一个项时,它将每一个项加总到一个总和并在迭代完成时返回总和

复制代码
#[test]
fn iterator_sum() {
    let v1 = vec![1, 2, 3];
    let v1_iter = v1.iter();
    
    // 调用 sum 之后不再允许使用 v1_iter 因为调用 sum 时它会获取迭代器的所有权
    let total: i32 = v1_iter.sum();
    assert_eq!(total, 6);
}

迭代器适配器

将当前迭代器变为不同类型的迭代器。

可以链式调用多个迭代器适配器。

因为所有迭代器都惰性,必须调用一个消费适配器方法以便获取迭代器适配器调用的结果

使用闭包自定义行为同时,复用 Iterator trait 提供的迭代行为

复制代码
let v1: Vec<i32> = vec![1, 2, 3];
// 调用 map 方法创建一个新迭代器,调用 collect 方法消费新迭代器并创建一个 vector
let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();
assert_eq!(v2, vec![2, 3, 4]);

// 编译如下代码将会warn,因为惰性迭代器没人使用,需要像上述那样,用collect触发下使用
// v1.iter().map(|x| x + 1);

使用闭包获取环境

使用 filter 迭代器适配器和捕获环境的闭包

迭代器的 filter 方法获取一个使用迭代器的每一个项并返回布尔值的闭包。如果闭包返回 true,其值将会包含在 filter 提供的新迭代器中。

如果闭包返回 false,其值不会包含在结果迭代器中

复制代码
#[derive(PartialEq, Debug)]
struct Shoe {
    size: u32,
    style: String,
}

fn shoes_in_my_size(shoes: Vec<Shoe>, shoe_size: u32) -> Vec<Shoe> {
    // into_iter:获取一个vector 的所有权
    shoes.into_iter()
        // shoe_size 是环境,函数做不到,但是闭包可以做到
        // filter 将这个迭代器适配成一个只含有那些闭包返回 true 的元素的新迭代器
        .filter(|s| s.size == shoe_size)
        // 消费迭代器
        .collect()
}

#[test]
fn filters_by_size() {
    let shoes = vec![
        Shoe { size: 10, style: String::from("sneaker") },
        Shoe { size: 13, style: String::from("sandal") },
        Shoe { size: 10, style: String::from("boot") },
    ];

    let in_my_size = shoes_in_my_size(shoes, 10);

    assert_eq!(
        in_my_size,
        vec![
            Shoe { size: 10, style: String::from("sneaker") },
            Shoe { size: 10, style: String::from("boot") },
        ]
    );
}

创建自定义迭代器

实现 Iterator trait

只会从 1 数到 5 的迭代器

复制代码
struct Counter {
    count: u32,
}

impl Counter {
    fn new() -> Counter {
        Counter { count: 0 }
    }
}

impl Iterator for Counter {
    type Item = u32;

    fn next(&mut self) -> Option<Self::Item> {
        self.count += 1;

        if self.count < 6 {
            Some(self.count)
        } else {
            None
        }
    }
}

#[test]
fn calling_next_directly() {
    let mut counter = Counter::new();

    assert_eq!(counter.next(), Some(1));
    assert_eq!(counter.next(), Some(2));
    assert_eq!(counter.next(), Some(3));
    assert_eq!(counter.next(), Some(4));
    assert_eq!(counter.next(), Some(5));
    assert_eq!(counter.next(), None);
}

其他 Iterator trait 方法使用自定义迭代器的next

复制代码
#[test]
fn using_other_iterator_trait_methods() {
    let sum: u32 = Counter::new()
        // 获取 Counter 实例产生的值
        // 将这些值与另一个 Counter 实例在省略了第一个值之后产生的值配对
        .zip(Counter::new().skip(1))
        // 将每一对值相乘
        .map(|(a, b)| a * b)
        // 只保留那些可以被3整除的结果
        .filter(|x| x % 3 == 0)
        // 将所有保留的结果相加
        .sum();
    assert_eq!(18, sum);
}

zip 只产生四对值;

第五对值 (5, None) 从未被产生

zip 在任一输入迭代器返回 None 时也返回 None

相关推荐
小嘉丶学长3 小时前
Day14 · 字典与集合
java·开发语言·网络·python·学习·1024程序员节
niyongsheng3 小时前
3D 装箱算法工程化:数据结构、放置启发式与支撑规则
数据结构·算法·rust
小李不想当小白3 小时前
SPI通信外设与W25Q64 (硬件SPI)(STM32标准库学习笔记)
笔记·stm32·单片机·嵌入式硬件·学习·嵌入式
Hello.Reader4 小时前
tsrs 技术解析用 Rust 移植 TypeScript 7,类型检查还能怎样提速?
开发语言·rust·typescript
王仔小奶6 小时前
Rust 正在接管金融圈?深度拆解一份高薪 FinTech 岗位 JD
开发语言·金融·rust
adinnet20267 小时前
电商客服对话知识库的自学习闭环与质量演进机制
学习
你为她披上外套时我正站在窗外7 小时前
subtitler v2.10 发布:3 个社区 PR、一个一天修掉的 issue,和关于 uuencode 的真相
rust
Thneonl7 小时前
熔断不是开关是状态机:Claude Code 路由内核的 3 条红线
人工智能·rust
Thneonl7 小时前
一个 Claude Code 本地代理,凭啥配了 3 个观测出口?
人工智能·rust
lazy H8 小时前
【计算机网络】第 13 篇:NAT 和端口映射是什么?为什么本地服务外网访问不了?
java·网络·网络协议·学习·计算机网络·408