Skip to content

循环和迭代

  • 循环: 是指在满足指定的条件的情况下, 重复的执行一段代码(需要满足指定条件才停止)
  • 迭代: 是指对一个序列中的元素逐个访问的过程(元素全部被消费后会自动停止)

迭代器

迭代器模式: 对一系列项目执行某些任务 迭代器就负责遍历每一个项

Rust 的迭代器: Rust 迭代器是惰性的, 除非显示调用, 否则迭代器本身没有任何作用

Rust
fn main() {
    let nums = vec![3, 2, 1];
    let mut iter = nums.iter();
    println!("iter: {:?}", iter); // 获取一个迭代器对象

    // 手动一次次迭代
    let item1 = iter.next();      // 手动调用 next 方法
    println!("item1: {:?}", item1); // Some(3)

    let item2 = iter.next();
    println!("item2: {:?}", item2); // Some(2)

    let item3 = iter.next();
    println!("item3: {:?}", item3); // Some(1)

    let item4 = iter.next();
    println!("item3: {:?}", item4); // None


    // 一次性全部遍历
    for item in nums.iter() {
        println!("item is: {:?}", item);
        // 依次输出: 3 2 1
    }
}

Iterator trait 和 next

所有迭代器都实现了 Iterator 特性, iterator trait 定义于标准库

rust
pub trait Iterator {
    // 先不用管这个语法是什么意思,
    // 只需要知道, 这个是 next 方法的返回值类型
    type Item;

    fn next(&mut self) -> Option<Self::Item>;

    // ... 迭代器的其他方法 ... //
}

消费迭代器的方法

Iterator 中实现了很多方法, 其中在这些方法内部调用 next 方法的 就叫做 消费适配器(consuming adaptors)

被迭代器迭代的元素会有限的, 被消费已给就相当于吃掉了一个

rust
fn main () {
    let items = vec![1, 2, 3, 4, 5];

    let iter = items.iter();

    let x: i32 = iter.sum(); // 会获取迭代器的所有权
    println!("sum = {}", x);

    // 无法再次使用 迭代器, 因为所有权在 sum 方法那里
    // for item in iter {
    //     println!("item = {}", item);
    // }

    // 这样是可以的, 因为这样是新创建了一个迭代器
    // 和原来那个迭代器(iter) 没有关系
    for item in items.iter() {
        println!("item = {}", item);
    }
}

迭代器适配器

所谓迭代器适配器(Iterator adaptors), 就是指可以将当前迭代器变为不同类型的迭代器, 可以链式调用多个迭代器, 但是 最后必须调用一个消费适配器方法 才能开始迭代, 因为所有的迭代器都是惰性的

rust
fn main () {
    let items = vec![1, 2, 3, 4, 5];

    let iter  = items.iter();
    println!("    iter: {:?}", iter);

    let map_iter = iter.map(|x| x + 1);

    // 可以看到 map_iter 并不是原来的 iter
    // 而且,他仅仅是返回了一个新的迭代器
    // 并没有执行出结果
    println!("map_iter: {:?}", map_iter);

    // 如果需要看到效果, 需要调用一个 消费适配器
    // 如果不调用
    let map_items: Vec<i32> = map_iter.collect();

    // 输出: [2, 3, 4, 5, 6]
    println!("map_items: {:?}", map_items);
}

自定义迭代器

要自定义迭代器就必须实现 Iterator 这个 trait

rust
#[derive(Debug)]
struct Stack<T> {
    items: Vec<T>,
}

impl<T> Stack<T> {
    pub fn new() -> Self {
        Self { items: Vec::new() }
    }
    pub fn push(&mut self, item: T) {
        self.items.push(item);
    }
    pub fn pop(&mut self) -> T {
        self.items.pop().unwrap()
    }
}

impl<T> Iterator for Stack<T> {
    type Item = T;

    // 只需要实现 next 方法即可
    fn next(&mut self) -> Option<Self::Item> {
        self.items.pop()
    }
}


fn main () {
    let mut stk = Stack::new();

    stk.push(1);
    stk.push(2);
    stk.push(3);

    for item in stk {
        println!("item: {}", item); // 3,2,1
    }
}

使用自定义迭代器上的其他方法

rust
fn main () {
    let mut stk = Stack::new();

    stk.push(1);
    stk.push(2);
    stk.push(3);

    // items 需要指定类型
    let items: Vec<i32> = stk.map(|x| x + 1).collect();

    // 4,3,2
    println!("{:?}", items);
}

迭代器和闭包的性能

迭代器是 Rust 的 零成本抽象(zero-cost abstractions)之一, 它意味着抽象并不会引入运行时开销

简单点来说就是: 虽然源码是这样写的, 但是最后还是会编译为循环, 所以不用担心性能问题

闭包和迭代器是 Rust 受函数式编程语言观念所启发的功能, 他们对 Rust 以底层的性能来明确的表达高级概念的能力有很大贡献, 闭包和迭代器的实现达到了不影响运行时性能的程度, 这正是 Rust 竭力提供零成本抽象的目标的一部分

这句话是Rust程序设计语言上的

Released under the MIT License.