【Rust中的迭代器】
Rust中的迭代器
- 什么是迭代
- 什么是迭代器
- C++中的迭代器
- Rust中的迭代器
- rust中的 iter,iter_mut, into_iter
- rust中的“指针移动”
- rust中的IntoIterator 特征实现
- collect
- enumerate可以拿到索引
- 总结
什么是迭代
迭代是指,在一个过程中反复重复,不断推进。在计算机领域,迭代是重复执行某一个过程的方法,每一次执行都基于上一次执行的结果。
什么是迭代器
Iterators are used to access and iterate through elements of data structures (vectors, sets, etc.), by “pointing” to them.
迭代器用于接入和迭代数据结构中的元素,通过"指向"他们,在几乎所有编程语言中,迭代器的功效均是如此。
C++中的迭代器
代码示例:
// 创建一个存储字符串的vector
vector<string> cars = {"Volvo", "BMW", "Ford", "Mazda"};// 声明一个字符串vector的迭代器(以下均称作iterator)
vector<string>::iterator it;//通过iterator循环获取数组中的字符串数据.
for (it = cars.begin(); it != cars.end(); ++it) {cout << *it << "\n";
}
上述代码中,第二行便是声明了一个迭代器,在循环中,通过begin()方法将vector中的第一个迭代器与之赋值,取到了指向数组中第一个元素的pointer,再通过for不断向后迭代,直至达到end(),即数组中最后一个元素的后一个位置,需要注意的是,begin()和end(),在代码中也可以看得出,他们并不是属于迭代器方法,而属于数据结构。
Rust中的迭代器
实现了Iterator特征的是迭代器,如果开发者需要特定的迭代方法,需要自己实现Iterator特征
代码示例:
fn main() {let vec = vec![1, 2, 3];let veciter = vec.iter(); //<--迭代器初始化for val in veciter {println!("{}", val);}
}
rust中迭代器的初始化均是由是由迭代器的数据结构初始化赋值.如上述代码中的vec.iter();,具体的,我们可以进入到源码看看iter做了什么。
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T, A: Allocator> IntoIterator for &'a Vec<T, A> {type Item = &'a T;type IntoIter = slice::Iter<'a, T>;fn into_iter(self) -> Self::IntoIter {self.iter()}
}
再进一步观察self.iter()源码:
#[stable(feature = "rust1", since = "1.0.0")]#[inline]pub fn iter(&self) -> Iter<'_, T> {Iter::new(self)}
iter通过调用Iter::new(self),从而初始化了一个数组迭代器.进一步看Iter::new(Self):
impl<'a, T> Iter<'a, T> {#[inline]pub(super) fn new(slice: &'a [T]) -> Self {let len = slice.len();let ptr: NonNull<T> = NonNull::from(slice).cast();// SAFETY: Similar to `IterMut::new`.unsafe {let end_or_len =if T::IS_ZST { without_provenance(len) } else { ptr.as_ptr().add(len) };Self { ptr, end_or_len, _marker: PhantomData }}}
可以看到的是,new中也是获取了切片长度和指针,这样在迭代过程中,指针安全高效的执行每一次迭代并找到数据.
rust中的 iter,iter_mut, into_iter
iter:引用,iter_mut:可变引用,into_iter:夺取所有权(如果你只想遍历一次后不再使用)
rust中的“指针移动”
C++中的迭代器,通过pointer向后移动来迭代数据,rust则通过next:
#[inline]fn next(&mut self) -> Option<$elem> {// could be implemented with slices, but this avoids bounds checks// SAFETY: The call to `next_unchecked` is// safe since we check if the iterator is empty first.unsafe {if is_empty!(self) {None} else {Some(self.next_unchecked())}}}
每一次的向后迭代数据,都会执行一次next,而next的执行总是安全的,它先会检查数据是否为空,不为空则返回下一个Option元素。
rust中的IntoIterator 特征实现
在上述源码Vec.iter()不难发现,rust为vec实现了IntoIterator 特征,分别对应于三种所有权模式,而IntoIterator 特征也不难理解,即将vec转换到迭代器,成为迭代器便可以迭代数组中的数据。
collect
Transforms an iterator into a collection.
collect() can take anything iterable, and turn it into a relevant collection. This is one of the more powerful methods in the standard library, used in a variety of contexts.
The most basic pattern in which collect() is used is to turn one collection into another. You take a collection, call iter on it, do a bunch of transformations, and then collect() at the end.
collect() can also create instances of types that are not typical collections. For example, a String can be built from chars, and an iterator of Result<T, E> items can be collected into Result<Collection, E>. See the examples below for more.
Because collect() is so general, it can cause problems with type inference. As such, collect() is one of the few times you’ll see the syntax affectionately known as the ‘turbofish’: ::<>. This helps the inference algorithm understand specifically which collection you’re trying to collect into.
简单来说,collect()可以接受任何可迭代的东西,并将其转换为相关的集合。使用collect()的最基本模式是将一个集合转换为另一个集合。你取一个集合,调用iter,做一堆转换,然后在最后取collect()。
代码示例:
use std::collections::VecDeque;
fn main() {let a = [1, 2, 3];let doubled: VecDeque<i32> = a.iter().map(|&x| x * 2).collect();assert_eq!(2, doubled[0]);assert_eq!(4, doubled[1]);assert_eq!(6, doubled[2]);
}
上述代码既将vec转换成VecDeque。
enumerate可以拿到索引
如果开发者想要使用到数据结构中的索引,使用enumerate可以做到
代码示例:
fn main() {let a = ['a', 'b', 'c'];let mut iter = a.iter().enumerate();assert_eq!(iter.next(), Some((0, &'a')));assert_eq!(iter.next(), Some((1, &'b')));assert_eq!(iter.next(), Some((2, &'c')));assert_eq!(iter.next(), None);
}
总结
迭代器可定制化,通常用于数据结构的迭代遍历,有很多方便的方法使用用以实现不同的目的,可以参考rust std.
“我们都无法确定选择是否正确,只是努力把选择变得正确.”