其他的多数语言中的, 数组直接就是可迭代的,无论是下标遍历还是迭代器迭代,都可以运行,所以刚开始用Rust的时候就翻车了。
像这样子:
let arr = [1,2,3];
for i in arr {
println!("{}", i);
}
好在Rust的编译器会详细的告诉我:
error[E0277]: `[{integer}; 3]` is not an iterator
--> src/main.rs:20:14
|
20 | for i in arr {
| ^^^ borrow the array with `&` or call `.iter()` on it to iterate over it
|
= help: the trait `std::iter::Iterator` is not implemented for `[{integer}; 3]`
= note: arrays are not iterators, but slices like the following are: `&[1, 2, 3]`
= note: required by `std::iter::IntoIterator::into_iter`
数组是不能直接使用for进行迭代的,必须使用数组引用或者获取数组的迭代器。因为for只能对实现了迭代器(std::iter::Iterator)trait的类型遍历。
通过错误信息中不难看出,Rust给出的建议是使用数组引用或者调用iter()方法来使数组获得迭代器能力。
在探讨这上述两种形式之前来认识两个trait。
std::iter::IntoIterator 和 std::iter::Iterator
IntoIterator
pub trait IntoIterator {
type Item;
type IntoIter: Iterator<Item=Self::Item>;
fn into_iter(self) -> Self::IntoIter;
}
其中含有两个类型定义,一个方法,主要功能获取一个迭代器,在for中,会自动使用std::iter::Iterator::into_iter()来获取类型的迭代器。
Iterator
pub trait Iterator {
type Item;
fn next(self) -> Option<Self::Item>;
//...其他方法
}
迭代器trait,实现此trait后即可使用for迭代,当next()返回None时则停止迭代。
了解了两个trait的作用后下面来探讨两种数组迭代形式:
数组引用形式:
let arr: [i32; 10] = [1; 10];
for i in &arr {
//loop body
}
&arr的类型是&[i32; 10],标准库libcore/array.rs中对数组引用实现了IntoIterator。
impl<'a, T, const N: usize> IntoIterator for &'a [T; N]
where
[T; N]: LengthAtMost32,
{
type Item = &'a T;
type IntoIter = Iter<'a, T>;
fn into_iter(self) -> Iter<'a, T> {
self.iter()
}
}
IntoIterator返回了一个Iter结构体。
pub struct Iter<'a, T: 'a> {
ptr: *const T,
end: *const T, // If T is a ZST, this is actually ptr+len. This encoding is picked so that
// ptr == end is a quick test for the Iterator being empty, that works
// for both ZST and non-ZST.
_marker: marker::PhantomData<&'a T>,
}
Iter<T>结构体通过 iterator! 宏实现了Iterator(位于libcore/slice/mod.rs中),所以通过for可以直接进行迭代。
但是数组引用的迭代方式有一个限制。
上面数组引用的实现IntoIterator中有一个trait bound,LengthAtMost32:从名称上可以看出是长度最大为32。
LengthAtMost32是利用array_impl! 宏进行实现。
macro_rules! array_impls {
($($N:literal)+) => {
$(
#[unstable(feature = "const_generic_impls_guard", issue = "0")]
impl<T> LengthAtMost32 for [T; $N] {}
)+
}
}
array_impls! {
0 1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
30 31 32
}
通过这个宏为多个数组类型实现LengthAtMost32。也就是说数组引用的形式最多支持到[T; 32]类型。
let arr = [1; 33];
for i in &arr {
println!("{}", i);
}
以上的代码编译不通过,因为[T; 33]并未实现LengthAtMost32,所以&[T; 33]及长度更大的数组类型就没有实现IntoIterator,这也就是数组引用的形式的一个限制。
error[E0277]: the trait bound `&[{integer}; 33]: std::iter::IntoIterator` is not satisfied
--> src/main.rs:29:14
|
29 | for i in &a {
| ^^ the trait `std::iter::IntoIterator` is not implemented for `&[{integer}; 33]`
|
= help: the following implementations were found:
<&'a [T; _] as std::iter::IntoIterator>
<&'a [T] as std::iter::IntoIterator>
<&'a mut [T; _] as std::iter::IntoIterator>
<&'a mut [T] as std::iter::IntoIterator>
= note: required by `std::iter::IntoIterator::into_iter`
调用iter()方法:
既然数组引用有限制,那么就改成iter()
let arr = [1; 33];
for i in arr.iter() {
println!("{}", i);
}
arr = [1; 33];for i in arr.iter() { println!("{}", i);}可是在libcore/array.rs中并没有对[T; N]实现iter()方法。通过IDE的定义跳转可以找到iter()是libcore/slice/mod.rs中,对slice [T] 类型实现的方法。这一点在std/primitive.array.html中有提到:当数组调用slice方法时会强制转换为slice类型,反之则不会,因为slice是动态的大小,数组是固定大小,不能由slice转换到数组。
所以arr.iter()相当于(arr[..]).iter() 或 (&a[..]).iter()。
iter()方法在libcore/slice/mod.rs定义
impl<T> [T] {
//...
pub fn iter(&self) -> Iter<'_, T> {
unsafe {
let ptr = self.as_ptr();
assume(!ptr.is_null());
let end = if mem::size_of::<T>() == 0 {
(ptr as *const u8).wrapping_add(self.len()) as *const T
} else {
ptr.add(self.len())
};
Iter {
ptr,
end,
_marker: marker::PhantomData
}
}
}
//...
}
同样返回Iter<T>结构体,与数组引用实现的IntoIterator返回的迭代器一致。
总结:
- 数组由于没有实现迭代器,而不能实现for遍历,但是可以使用loop或者while通过下标遍历。
- 通过数组引用进行遍历,存在最大长度限制,超过32就无法直接遍历了。
- 建议使用iter()方法获取Iterator迭代器,Iterator迭代器中包含很多方法,位于libcore/iter/traits/iterator.rs,而且Iter<T>还实现了许多其他的功能,例如DoubleEndedIterator等。