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 小时前
设计模式学习整理
学习·设计模式
湘-枫叶情缘3 小时前
解构认知边界:论万能方法的本体论批判与方法论重构——基于跨学科视阈的哲学-科学辩证
科技·学习·重构·生活·学习方法
inputA5 小时前
【LwIP源码学习6】UDP部分源码分析
c语言·stm32·单片机·嵌入式硬件·网络协议·学习·udp
海尔辛5 小时前
学习黑客5 分钟读懂Linux Permissions 101
linux·学习·安全
Python私教5 小时前
征服Rust:从零到独立开发的实战进阶
服务器·开发语言·rust
真的想上岸啊6 小时前
学习51单片机01(安装开发环境)
嵌入式硬件·学习·51单片机
每次的天空6 小时前
Android学习总结之Glide自定义三级缓存(面试篇)
android·学习·glide
名誉寒冰7 小时前
# KVstorageBaseRaft-cpp 项目 RPC 模块源码学习
qt·学习·rpc
开发游戏的老王8 小时前
[虚幻官方教程学习笔记]深入理解实时渲染(An In-Depth Look at Real-Time Rendering)
笔记·学习·虚幻
码农小嘉9 小时前
若依框架页面
学习