likes
comments
collection
share

【Rust 进阶教程】 03 详解迭代器(2)

作者站长头像
站长
· 阅读数 22

0x00 开篇

在 《学习日记 21课》我们仅仅是简单介绍了迭代器的基础使用方法。在平时的工作中,迭代器的使用频率是非常高的,再加上 Rust 所有权的概念,这就可能导致迭代器的理解有了一定的难度。本篇文章我们将更深入的认识迭代器。本篇文章阅读时间大约 5 分钟

0x01 迭代器常见术语

我先总结下在迭代器中出现的常见术语吧。

  • 迭代器 (iterator): 指任何实现 Iterator 的类型。
  • 可迭代类型 (iterable): 指实现 IntoIterator 的类型,调用 into_iter 可以获取迭代器。
  • 迭代项 (item): 指迭代器产生的值。
  • 消费者 (consumer): 指接收迭代器产生的迭代项的代码。

0x02 iter 和 iter_mut

常见的集合类型都提供了 iter  和 iter_mut 方法。通过这两个方法可以创建集合的迭代器。

iter : 产生每个迭代项的共享引用(不可变引用)

iter_mut : 产生每个迭代项的可修改引用

下面代码演示了通过 iter 或者 iter_mut 方法创建迭代器,通过 iter_mut 方法创建迭代器后并修改 vec 中的第一个元素。当然我们也可以使用 for 来迭代迭代器。

fn main() {
    // iter
    let vec = vec!["hello", "my", "name", "is", "rust", "!"];
    let mut my_iter = vec.iter();

    println!("{:?}", my_iter.next());
    println!("{:?}", my_iter.next());
    println!("{:?}", my_iter.next());

    // iter_mut
    let mut vec = vec!["hello", "my", "name", "is", "rust", "!"];
    let mut my_iter_mut = vec.iter_mut();
    
    println!("{:?}", my_iter_mut.next());
    println!("{:?}", my_iter_mut.next());
    println!("{:?}", my_iter_mut.next());
    
    // for + iterator
    let mut vec = vec!["hello".to_string(), "rust".to_string(), "!".to_string()];
    for item in vec.iter() {
        println!("{:?}", item);
    }
}
// 运行结果
// Some("hello")
// Some("rust")
// Some("!")
// Some("rust")
// Some("!")
// "hello!"
// "hello"
// "rust"
// "!"

0x03 into_iter

如果一个类型实现了 IntoIterator 则可以调用它的 into_iter 方法。我们在上一篇文章也介绍了它,我们常用的 for 循环就是调用的它。如果集合里的类型是非 Copy 类型,消费者在取得每个值后,在迭代器被清除后,集合里的元素也会被清除。集合会只剩“空壳”,当然剩下的“空壳”也会被清除

fn main() {
	// into_iterator
    let mut vec = vec!["hello".to_string(), "rust".to_string(), "!".to_string()];
    let mut iter = vec.into_iter();
    // 集合里的第1个元素所有权转移至 first
    let first = iter.next();
    // 集合里的第2个元素所有权转移至 second
    let second = iter.next();
    // 集合里的第3个元素所有权转移至 second
    let third = iter.next();

    // 至此 vec 已变成空壳
    // vec 无法再使用,所有权已经转移
    // 下面的代码将会编译失败
    // println!("{:?}", vec);
}

另外,当集合的引用调用 into_iter 方法时:

  • 共享引用(不可变引用):into_iter 会产生迭代项的共享引用的迭代器。
  • 可修改引用:into_iter 会产生可修改引用的迭代器。

示例代码如下:

fn main() {
    // 共享引用
    let vec: Vec<String> = vec!["hello".to_string(), "rust".to_string(), "!".to_string()];
    let mut iter = (&vec).into_iter();
    println!("{:?}", iter.next());
    // 下面的代码并不会编译失败
    println!("{:?}", vec);
    
    // 可修改引用
    let mut vec: Vec<String> = vec!["hello".to_string(), "rust".to_string(), "!".to_string()];
    let mut iter = (&mut vec).into_iter();
    iter.next().unwrap().push_str("hello");
    // 下面的代码并不会编译失败
    println!("{:?}", vec);
}
// 运行结果
// Some("hello")
// ["hello", "rust", "!"]
// ["hellohello", "rust", "!"]

换句话说,下面几种情况是等价的。

// 共享引用
for item in &vec {} 调用了 (&vec).into_iter()

// 可修改引用 
for item in &mut vec {} 调用了 (&mut vec).into_iter()

// 按值传递(非Copy类型转移所有权)
for item in vec {} 调用了 mut vec.into_iter() 

0x04 iter、iter_mut、into_iter的区别

有读者看到这里,可能已经蒙了,这么多迭代器的区别在哪里呢?

对于非引用(包含切片)类型:

  • vec.iter 等价于 (&vec).into_iter
  • vec.iter_mut 等价于 (&mut vec).iter

其实对比源码,你也会发现他们是等价的。iter 和 (&vec).into_iter 的返回值都是 Iter<T> 类型,vec.iter_mut 和 (&mut vec).iter 返回值是 IterMut<T> 类型。**并不是所有类型的都会给出三种迭代方式。**比如:HashMap 等类型。

对于引用类型,由于他们没有所有权。所以切片基本只实现了两个迭代器 iter 和 iter_mut

 0x05 小结

本篇文章比较简单,算是对上一篇文章的一个补充。另外在集合里面还有非常多的适配器方法,我也就不一一介绍了。