Zach的博客

Slice和Iterator

Contiguous regions of memory

Rust用以下三种类型来处理连续的内存:

  • Vec\:可变大小的堆向量数组
  • [T; N]:编译器确定大小的数组
  • [T]:用于代表连续内存的slice

一个Slice只能通过特定类型的指针来操作,指针类型为以下三种:

  • &[T]
  • &mut [T]
  • Box<[T]>

如果我们有一个Vec<T>变量,那么我们可以通过下面的方法得到对应的Slice:

1
2
let vec = vec![12, 3, 4];
let int_slice = &vec[..];

当然也可以直接创建一个Slice变量:

1
let str_slice: &[&str] = &["one", "two", "three"];

[T; N]

[T; N]是在编译器就确定长度的数组,有两种方式创建这种数组:

  • [x, y, z]
  • [x; N]:x被重复N次

如果数组的长度小于等于32,那么如果数组的元素类型允许(即元素类型实现了对应的Trait),那么数组自动实现以下Trait:

  • CloneT: Copy
  • Debug
  • IntoIterator&[T; N]&mut [T; N]实现了这个Trait)
  • PartialEqPartialOrdOrdEq
  • Hash
  • AsRefAsMut
  • BorrowBorrowMut
  • Default

之所以有长度的限制,是因为Rust不支持在数组长度上的泛型,所以就实现到了长度32为止。

Iterator

迭代器可以说在Rust中占有重要的位置,在日常编码过程中我们经常会用到迭代器。这里我只是记录几点自己看标准库文档的心得,具体使用还是请看标准库文档吧。

编写一个迭代器

编写一个迭代器很简单,我们只要实现Iterator这个Trait中的next方法就好了,其余的方法我们都可以免费获得:

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
struct Counter {
count: usize,
}

impl Counter {
fn new() -> Counter {
Counter { count: 0 }
}
}

impl Iterator for Counter {
type Item = usize;

fn next(&mut self) -> Option<usize> {
self.count += 1;
if self.count < 6 {
Some(self.count)
} else {
None
}
}
}

let mut counter = Counter::new();

let x = counter.next().unwrap();
println!("{}", x);

let x = counter.next().unwrap();
println!("{}", x);

再来看一下Iterator这个Trait:

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;

fn size_hint(&self) -> (usize, Option<usize>) { ... }
fn count(self) -> usize { ... }
fn last(self) -> Option<Self::Item> { ... }
fn nth(&mut self, n: usize) -> Option<Self::Item> { ... }
fn chain<U>(self, other: U) -> Chain<Self, U::IntoIter> where U: IntoIterator<Item=Self::Item> { ... }
fn zip<U>(self, other: U) -> Zip<Self, U::IntoIter> where U: IntoIterator { ... }
fn map<B, F>(self, f: F) -> Map<Self, F> where F: FnMut(Self::Item) -> B { ... }
fn filter<P>(self, predicate: P) -> Filter<Self, P> where P: FnMut(&Self::Item) -> bool { ... }
fn filter_map<B, F>(self, f: F) -> FilterMap<Self, F> where F: FnMut(Self::Item) -> Option<B> { ... }
fn enumerate(self) -> Enumerate<Self> { ... }
fn peekable(self) -> Peekable<Self> { ... }
fn skip_while<P>(self, predicate: P) -> SkipWhile<Self, P> where P: FnMut(&Self::Item) -> bool { ... }
fn take_while<P>(self, predicate: P) -> TakeWhile<Self, P> where P: FnMut(&Self::Item) -> bool { ... }
fn skip(self, n: usize) -> Skip<Self> { ... }
fn take(self, n: usize) -> Take<Self> { ... }
fn scan<St, B, F>(self, initial_state: St, f: F) -> Scan<Self, St, F> where F: FnMut(&mut St, Self::Item) -> Option<B> { ... }
fn flat_map<U, F>(self, f: F) -> FlatMap<Self, U, F> where F: FnMut(Self::Item) -> U, U: IntoIterator { ... }
fn fuse(self) -> Fuse<Self> { ... }
fn inspect<F>(self, f: F) -> Inspect<Self, F> where F: FnMut(&Self::Item) -> () { ... }
fn by_ref(&mut self) -> &mut Self { ... }
fn collect<B>(self) -> B where B: FromIterator<Self::Item> { ... }
fn partition<B, F>(self, f: F) -> (B, B) where B: Default + Extend<Self::Item>, F: FnMut(&Self::Item) -> bool { ... }
fn fold<B, F>(self, init: B, f: F) -> B where F: FnMut(B, Self::Item) -> B { ... }
fn all<F>(&mut self, f: F) -> bool where F: FnMut(Self::Item) -> bool { ... }
fn any<F>(&mut self, f: F) -> bool where F: FnMut(Self::Item) -> bool { ... }
fn find<P>(&mut self, predicate: P) -> Option<Self::Item> where P: FnMut(&Self::Item) -> bool { ... }
fn position<P>(&mut self, predicate: P) -> Option<usize> where P: FnMut(Self::Item) -> bool { ... }
fn rposition<P>(&mut self, predicate: P) -> Option<usize> where P: FnMut(Self::Item) -> bool, Self: ExactSizeIterator + DoubleEndedIterator { ... }
fn max(self) -> Option<Self::Item> where Self::Item: Ord { ... }
fn min(self) -> Option<Self::Item> where Self::Item: Ord { ... }
fn max_by_key<B, F>(self, f: F) -> Option<Self::Item> where B: Ord, F: FnMut(&Self::Item) -> B { ... }
fn max_by<F>(self, compare: F) -> Option<Self::Item> where F: FnMut(&Self::Item, &Self::Item) -> Ordering { ... }
fn min_by_key<B, F>(self, f: F) -> Option<Self::Item> where B: Ord, F: FnMut(&Self::Item) -> B { ... }
fn min_by<F>(self, compare: F) -> Option<Self::Item> where F: FnMut(&Self::Item, &Self::Item) -> Ordering { ... }
fn rev(self) -> Rev<Self> where Self: DoubleEndedIterator { ... }
fn unzip<A, B, FromA, FromB>(self) -> (FromA, FromB) where FromA: Default + Extend<A>, FromB: Default + Extend<B>, Self: Iterator<Item=(A, B)> { ... }
fn cloned<'a, T>(self) -> Cloned<Self> where Self: Iterator<Item=&'a T>, T: 'a + Clone { ... }
fn cycle(self) -> Cycle<Self> where Self: Clone { ... }
fn sum<S>(self) -> S where S: Sum<Self::Item> { ... }
fn product<P>(self) -> P where P: Product<Self::Item> { ... }
fn cmp<I>(self, other: I) -> Ordering where I: IntoIterator<Item=Self::Item>, Self::Item: Ord { ... }
fn partial_cmp<I>(self, other: I) -> Option<Ordering> where I: IntoIterator, Self::Item: PartialOrd<I::Item> { ... }
fn eq<I>(self, other: I) -> bool where I: IntoIterator, Self::Item: PartialEq<I::Item> { ... }
fn ne<I>(self, other: I) -> bool where I: IntoIterator, Self::Item: PartialEq<I::Item> { ... }
fn lt<I>(self, other: I) -> bool where I: IntoIterator, Self::Item: PartialOrd<I::Item> { ... }
fn le<I>(self, other: I) -> bool where I: IntoIterator, Self::Item: PartialOrd<I::Item> { ... }
fn gt<I>(self, other: I) -> bool where I: IntoIterator, Self::Item: PartialOrd<I::Item> { ... }
fn ge<I>(self, other: I) -> bool where I: IntoIterator, Self::Item: PartialOrd<I::Item> { ... }
}

我们必须要实现的就只是next方法,其余的都是构建在next之上,也就是说我们可以直接使用其余的方法。

Collections to Iterator

有三种方法得到一个集合的迭代器:

  • iter:迭代类型为&T
  • iter_mut:迭代类型为&mut T
  • into_iter:迭代类型为T

前两个方法是Slice中定义的方法,因为一个集合是一段连续的内存,所以可以调用前面两个方法。第三个方法是TraitIntoIterator中定义的接口:

1
2
3
4
5
pub trait IntoIterator where Self::IntoIter::Item == Self::Item {
type Item;
type IntoIter: Iterator;
fn into_iter(self) -> Self::IntoIter;
}

这里有一个地方需要注意,对于一个集合,比如说Vec<T>,我们在该变量上调用into_iter,那么迭代的对象是集合的类型,在迭代之后变量的所有权就被转移了:

1
2
3
4
5
6
let v = vec![1, 2, 3];
for x in v.into_iter() {
x;
}
// error
// v;

但是如果是一个Slice呢,在它上面调用into_iter,迭代的对象实际上是&T

1
2
3
4
5
6
let v = &[1, 2, 3];
for x in v.into_iter() {
println!("{}", x);
}
// will compile
v;

这是为什么呢?我们来看一下Slice文档中关于IntoIterator这个Trait的实现说明:

1
impl<'a, T> IntoIterator for &'a [T]
type Item = &'a T
	The type of the elements being iterated over
type IntoIter = Iter<'a, T>
	Which kind of iterator are we turning this into?

只有一句话写在那里我们也不能很相信它,于是再来看看std::iter::Iter这结构中关于Iterator 实现的说明:

1
impl<'a, T> Iterator for Iter<'a, T>
type Item = &'a T

The type of the elements being iterated over.

fn next(&mut self) -> Option<&'a T>

可以看到Iterator这个Trait的关联类型是&T,所以我们迭代的对象的类型实际上是&T,所以在迭代之后Slice的所有权并没有转移。