【Rust 进阶教程】 03 详解迭代器(2)
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 小结
本篇文章比较简单,算是对上一篇文章的一个补充。另外在集合里面还有非常多的适配器方法,我也就不一一介绍了。
转载自:https://juejin.cn/post/7229899593260580919