迭代器答应咱们迭代一个接连的调集,例如数组、动态数组 Vec、HashMap 等,在此过程中,只需关怀调集中的元素怎么处理,而无需关怀怎么开端、怎么结束、依照什么样的索引去拜访等问题。
For循环与迭代器
迭代器与 for 循环最主要的不同就在于:是否经过索引来拜访调集。严格来说,Rust 中的 for 循环是编译器供给的语法糖,终究还是对迭代器中的元素进行遍历。在 Rust 中,完结了IntoIterator
trait 的类型都能够主动把类型调集转换为迭代器,然后经过 for 语法糖进行拜访。
例如数组:
let arr = [1, 2, 3];
for v in arr {
peintln!("{}", v);
}
也是能够运用IntoIterator
trait的into_iter
办法显式地将数组转换成迭代器。
let arr = [1, 2, 3];
for v in arr.into_iter() {
println!("{}", v);
}
注:数组不是迭代器(没有完结Iterator
),但是数组完结了IntoIterator
,Rust 经过 for 语法糖,主动把完结了该特征的数组类型转换为迭代器,终究让咱们能够直接对一个数组进行迭代。
此外,还能够运用 for 循环对数值序列进行迭代,如
for i in 1..100 { ... };// 有限迭代器:对有限数值序列进行迭代
for i in (1..).into_iter() {...}; // 无限迭代器:无限长度的自增序列
Iteratortrait 迭代器
Iterator
trait 界说:
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
// 省略其余有默许完结的办法
}
在 Rust 中,迭代器是慵懒的,也便是在界说迭代器后不运用将不会发生任何事,只有在运用时迭代器才会开端迭代其间的元素。
慵懒初始化的办法确保了创立迭代器不会有任何额外的功能损耗,其间的元素也不会被耗费,只有运用到该迭代器的时分,全部才开端。
迭代器之所以成为迭代器,便是由于完结了Iterator
trait,要完结该 trait,最主要的便是要完结其间的next
办法。for 循环经过不断调用迭代器上的next
办法来获取迭代器中的元素。
比方:
let arr = [1, 2, 3];
let mut arr_iter = arr.into_iter();
assert_eq!(arr_iter.next(), Some(1));
assert_eq!(arr_iter.next(), Some(2));
assert_eq!(arr_iter.next(), Some(3));
assert_eq!(arr_iter.next(), None);
迭代器自身也能够直接调用next
办法,回来值是 Option 类型(有值时是Some(T),无值时是None),遍历是依照迭代器中元素的排列顺序顺次进行的,同时手动迭代必须将迭代器声明为mut
可变,由于调用next
会改动迭代器其间的状况数据,运用 for 循环时则无需标注,由于for循环主动完结。
for
循环是迭代器的语法糖,大约原理如下:
let values = vec![1, 2, 3];
{
/// IntoIterator::into_iter完全限定的办法与into_iter办法values.into_iter()是等价的
let result = match IntoIterator::into_iter(values) {
/// 运用了loop循环配合next办法来遍历迭代器中的元素,当迭代器回来None时,跳出循环
mut iter => loop {
match iter.next() {
Some(x) => { println!("{}", x); },
None => break,
}
},
};
result
}
总归,next
办法对迭代器的遍历是耗费性的,每次耗费它一个元素,终究迭代器中将没有任何元素,只能回来None
。
IntoIterator trait 转化成迭代器
IntoIterator
trait 界说:
trait IntoIterator
where
<Self::IntoIter as Iterator>::Item == Self::Item,
{
type Item;
type IntoIter: Iterator;
fn into_iter(self) -> Self::IntoIter;
}
完结IntoIterator
特性的类型能够被转换为迭代器。当用于for-in
循环时,将主动调用该类型的into_iter
办法。
比方动态 Vec 不只完结了IntoIterator
trait,&Vec
与&mut Vec
相同如此。因而咱们能够相应的对可变与不可变的引证,以及自有值进行迭代。
Vec 完结了三种不同方式的 IntoInterator:
impl<T> IntoIterator for Vec<T>
impl<'a, T> IntoIterator for &'a Vec<T>
impl<'a, T> IntoIterator for &'a mut Vec<T>
依据被遍历目标的类型,for 循环会挑选运用哪个into_iter
办法并取得不同类型的元素:
- 可遍历目标的同享引证回来同享引证的元素 &T
- 可遍历目标的可变引证回来可变引证的元素 &mut T
- 拥有所有权的可遍历目标回来值 T。留意:这个可遍历目标被搬运所有权,从而被会 drop 掉。
// vec = Vec<T>
for v in &vec {} // v = &T
// above example desugared
// 以上代码等价于
for v in (&vec).into_iter() {}
// vec = Vec<T>
for v in &mut vec {} // v = &mut T
// above example desugared
// 以上代码等价于
for v in (&mut vec).into_iter() {}
标准库的一揽子完结机制为迭代器Iterator
自身主动完结了IntoIterator
trait:
impl<I: Iterator> IntoIterator for I {
type Item = I::Item;
type IntoIter = I;
#[inline]
fn into_iter(self) -> I {
self
}
}
所以vec.into_iter()
与vec.into_iter().into_iter().into_iter()
效果相同的。
fn main() {
let values = vec![1, 2, 3];
for v in values.into_iter().into_iter().into_iter() {
println!("{}",v)
}
}
into_iter,iter,iter_mut 发生迭代器的差异
-
into_iter()
回来T,&T或 &mut T 类型的Iterator,依靠于环境; -
iter()
迭代引证(&T):调用next
办法回来的类型是Some(&T)
; -
iter_mut()
迭代可变引证(&mut T):调用next
办法回来的类型是Some(&mut T)
;
注:Rust命名规则:into_
之类的,都是拿走所有权,_mut
之类的都是可变引证,剩余的便是不可变引证。
代码示例:
fn main() {
let values = vec![1, 2, 3];
// 所有权搬运至迭代器中,values 将不能再运用
for v in values.into_iter() {
println!("{}", v)
}
// 下面的代码将报错,由于 values 的所有权在上面 `for` 循环中已经被搬运走
// println!("{:?}",values);
let values = vec![1, 2, 3];
let _values_iter = values.iter();
// 不会报错,由于 values_iter 仅仅借用了 values 中的元素
println!("{:?}", values);
let mut values = vec![1, 2, 3];
// 对 values 中的元素进行可变借用
let mut values_iter_mut = values.iter_mut();
// 取出第一个元素,并修改为0
if let Some(v) = values_iter_mut.next() {
*v = 0;
}
// 输出[0, 2, 3]
println!("{:?}", values);
}
Iterator 和 IntoIterator 的差异
Iterator
是迭代器 trait,只有完结了它才干称为迭代器,才干调用next
办法。
IntoIterator
着重的是某一个类型如果完结了该 trait,那么该类型数据能够经过into_iter()
、iter()
或iter_mut()
办法将其变成一个迭代器。
完结Iterator<Item = T>
的类型能够迭代发生T
类型。留意:并不存在IteratorMut
类型,由于能够经过在完结Iterator
特性时指定Item
关联类型,来挑选其回来的是不可变引证、可变引证还是自有值。
Vec<T> 办法 |
回来类型 |
---|---|
.iter() |
Iterator<Item = &T> |
.iter_mut() |
Iterator<Item = &mut T> |
.into_iter() |
Iterator<Item = T> |
顾客与适配器
顾客是迭代器上的办法,它会消费掉迭代器中的元素,然后回来该类型的值,这些顾客都有一个共同的特色:在界说中都依靠next
办法来消费元素。
消费性适配器:某些办法(比方collect
、sum
等)在其内部会主动调用next
办法,并会拿走迭代器的所有权,耗费掉迭代器上的元素。比方sum
办法,它会拿走迭代器的所有权,并重复调用next
来遍历迭代器并对里边的元素进行求和,并在迭代完结时回来总和。
fn main() {
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter();
let total: i32 = v1_iter.sum();
assert_eq!(total, 6);
// v1_iter 是借用了 v1,因而 v1 能够照常运用
println!("{:?}",v1);
// 以下代码会报错,由于 `sum` 拿到了迭代器 `v1_iter` 的所有权
// println!("{:?}",v1_iter);
}
迭代器适配器:Iterator
trait 中界说了另一类办法,答应咱们将当前迭代器变为不同类型的迭代器。能够链式调用多个迭代器适配器。不过由于所有的迭代器都是慵懒的,必须调用一个消费适配器办法以便获取迭代器适配器调用的结果。
let v1: Vec<i32> = vec![1, 2, 3];
v1.iter().map(|x| x + 1); // 正告,由于map办法回来一个迭代器,map 迭代器是慵懒的,不发生任何效果
let v2: Vec<> = v1.iter().map(|x| x + 1).collect(); // 将v1中的数顺次加1然后生成一个新的Vec,其间collect()办法便是一个顾客适配器
collect
办法是一个顾客适配器,能够将一个迭代器中的元素收集到指定类型中(留意:必须显式的告诉编译器想要收集成的调集类型),上面代码示例中的Vec<_>
表明将迭代器中的元素收集成 Vec 类型,详细元素类型经过类型推导取得。
map
办法是一个迭代器适配器,会将迭代器中的每一个值进行一系列操作,然后把该值转换成别的一个新值,该操作经过闭包完结。
let names = ["tim", "tony"];
let ages = [11, 33];
let folks: HashMap<_, _> = names.into_iter().zip(ages.into_iter()).collect();
zip
是一个迭代器适配器,其作用便是将两个迭代器的内容压缩到一同,构成Iterator<Item=(ValueFromA, ValueFromB)>
这样的新的迭代器,然后经过collect
办法将迭代器中的(K, V)方式的值收集成HashMap<K, V>
。
之前的map
办法中运用闭包作为迭代器适配器的参数,其最大的好处不只在于能够就地完结迭代器中元素的处理,还在于能够捕获环境值。下面的代码示例同时体现了这两个长处:
struct Shoe {
size: u32,
style: String,
}
fn shoes_in_size(shoes: Vec<Shoe>, shoe_size: u32) -> Vec<Shoe> {
shoes.into_iter().filter(|s| s.size == shoe_size).collect() //捕获外部环境中的变量 shoe_size 来与输入参数进行比较
}
filter
是迭代器适配器,用于对迭代器中的每个值进行过滤。它运用闭包作为参数,该闭包从迭代器中获取一项并回来一个bool
,如果闭包回来true
,其值将会包含在filter
供给的新迭代器中。
为自界说类型完结 Iterator trait
前面运用了数组来创立迭代器,但其实根据其它调集类型一样能够创立迭代器,也能够创立自己的迭代器——只要为自界说类型完结 Iterator trait 即可。中文标准库中的 Module std::iter 中给出了怎么完结迭代器的办法。
例如:
struct MyType {
items: Vec<String>
}
impl MyType {
fn iter(&self) -> impl Iterator<Item = &String> {
MyTypeIterator {
index: 0,
items: &self.items
}
}
}
struct MyTypeIterator<'a> {
index: usize,
items: &'a Vec<String>
}
impl<'a> Iterator for MyTypeIterator<'a> {
type Item = &'a String;
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.items.len() {
None
} else {
let item = &self.items[self.index];
self.index += 1;
Some(item)
}
}
}
完结自己的迭代器非常简略,但是Iterator
trait 中,只需要自己完结next
一个办法,由于其他办法都有默许完结,并且这些默许完结的办法其实都是根据next
办法完结的。
比方enumerate
是Iterator
trait 上的办法,该办法发生一个新的迭代器,其每个元素都是元素(索引,值)。enumerate
是迭代器适配器,能够运用顾客迭代器或 for 循环对新发生的迭代器进行处理。
代码示例如下:
let v = vec![1u64, 2, 3, 4, 5, 6];
for (i, v) in v.iter().enumerate() {
println!("第{}个值是{}", i, v)
}
迭代器的功能
依据测验,运用迭代器和 for 循环完结相同的任务,迭代器的运行时刻还要少一点。
迭代器是 Rust 的零本钱抽象之一,这就意味着抽象并不会引入运行时开销。
总归,迭代器是 Rust 受函数式言语启发而供给的高级言语特性,能够写出愈加简练、逻辑清晰的代码。编译器还能够经过循环展开(Unrolling)、向量化、消除边界检查等优化手法,使得迭代器和for
循环都有极为高效的履行功率。
总结
Rust 中完结了Iterator
trait 才是迭代器,完结IntoIterator
特性的类型能够被转换为迭代器,供给三个公共办法创立迭代器:iter()、iter_mut() 和 into_iter(),别离用于迭代 &T(引证)、&mut T(可变引证)和 T(值),其间,前两种是一般办法,而into_iter() 来自于IntoIteratortrait。
Rust 的 for 循环其实是迭代器语法糖,当没有显式的运用迭代器时,它会依据不同的上下文,别离运用 T、&T 和 &mut T 类型所完结的 into_iter() 回来的迭代器。
遍历类型回来不同遍历器的三个办法:
-
into_iter()
回来 T,&T 或 &mut T 类型的 Iterator,依靠于环境,IntoIterator
的办法; -
iter()
回来 Iterator<&T>,调用next
办法回来的类型是Some(&T)
; -
iter_mut()
回来 Iterator<&mut T>,调用next
办法回来的类型是Some(&mut T)
;
参考
- course.rs/advance/fun…
- kaisery.github.io/trpl-zh-cn/…
- my_lv.gitbooks.io/rust/conten…
- rustwiki.org/zh-CN/rust-…
- rust.ffactory.org/std/iter/in…
- www.becomebetterprogrammer.com/rust-iter-v…
- github.com/pretzelhamm…