自己写迭代器很简单,但要写得好需要几个小知识:结构体里放什么、怎样借用外部数据、要不要实现双端与精确长度、size_hint 的作用。这篇用四个例子逐条验证。
源码:
1 | struct Fib { a: u64, b: u64 }
2 | impl Fib { fn new() -> Self { Fib { a: 0, b: 1 } } }
3 | impl Iterator for Fib {
4 | type Item = u64;
5 | fn next(&mut self) -> Option<u64> {
6 | let r = self.a;
7 | let next = self.a.checked_add(self.b)?; // 溢出时返回 None,迭代结束
8 | self.a = self.b; self.b = next; Some(r)
9 | }
10 | }
11 | fn main() {
12 | println!("前 10 项:{:?}", Fib::new().take(10).collect::<Vec<_>>());
13 | println!("小于 100 的偶数项:{:?}", Fib::new().take_while(|&x| x < 100).filter(|x| x % 2 == 0).collect::<Vec<_>>());
14 | println!("一共有 {} 项(在 u64 溢出之前),最后一项 {:?}", Fib::new().count(), Fib::new().last());
15 | }
编译:成功
运行(退出码 0):
前 10 项:[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
小于 100 的偶数项:[0, 2, 8, 34]
一共有 92 项(在 u64 溢出之前),最后一项 Some(4660046610375530309)Iterator 的基本约定见Iterator Trait。
源码:
1 | struct Pairs<'a, T> { items: &'a [T], i: usize }
2 | impl<'a, T> Iterator for Pairs<'a, T> {
3 | type Item = (&'a T, &'a T);
4 | fn next(&mut self) -> Option<Self::Item> {
5 | if self.i + 1 >= self.items.len() { return None; }
6 | let p = (&self.items[self.i], &self.items[self.i + 1]); self.i += 1; Some(p)
7 | }
8 | }
9 | fn pairs<T>(items: &[T]) -> Pairs<'_, T> { Pairs { items, i: 0 } }
10 | fn main() {
11 | let words = vec!["a".to_string(), "b".to_string(), "c".to_string()];
12 | for (x, y) in pairs(&words) { println!("{x}-{y}"); }
13 | println!("差分:{:?}", pairs(&[1, 4, 9, 16]).map(|(a, b)| b - a).collect::<Vec<_>>());
14 | }
编译:成功
运行(退出码 0):
a-b
b-c
差分:[3, 5, 7] 编译:失败
error[E0277]: the trait bound `Count: DoubleEndedIterator` is not satisfied
--> <实验目录>/src/u53c.rs:6:53
|
6 | fn main() { println!("{:?}", Count { lo: 0, hi: 3 }.rev().collect::<Vec<_>>()); }
| ^^^ unsatisfied trait bound
|
help: the trait `DoubleEndedIterator` is not implemented for `Count`
--> <实验目录>/src/u53c.rs:1:1
|
1 | struct Count { lo: u32, hi: u32 }
| ^^^^^^^^^^^^
note: required by a bound in `rev`
--> <rust 标准库源码>/library/core/src/iter/traits/iterator.rs:3445:23
|
3443 | fn rev(self) -> Rev<Self>
| --- required by a bound in this associated function
3444 | where
3445 | Self: Sized + DoubleEndedIterator,
| ^^^^^^^^^^^^^^^^^^^ required by this bound in `Iterator::rev`
error[E0599]: the method `collect` exists for struct `Rev<Count>`, but its trait bounds were not satisfied
--> <实验目录>/src/u53c.rs:6:59
|
1 | struct Count { lo: u32, hi: u32 }
…(共 49 行)
源码:
1 | struct Count { lo: u32, hi: u32 }
2 | impl Iterator for Count {
3 | type Item = u32;
4 | fn next(&mut self) -> Option<u32> { if self.lo >= self.hi { None } else { self.lo += 1; Some(self.lo - 1) } }
5 | fn size_hint(&self) -> (usize, Option<usize>) { let n = (self.hi - self.lo) as usize; (n, Some(n)) }
6 | }
7 | impl DoubleEndedIterator for Count {
8 | fn next_back(&mut self) -> Option<u32> { if self.lo >= self.hi { None } else { self.hi -= 1; Some(self.hi) } }
9 | }
10 | impl ExactSizeIterator for Count {}
11 | fn main() {
12 | println!("rev:{:?}", Count { lo: 0, hi: 5 }.rev().collect::<Vec<_>>());
13 | println!("len:{},size_hint:{:?}", Count { lo: 2, hi: 9 }.len(), Count { lo: 2, hi: 9 }.size_hint());
14 | let mut c = Count { lo: 0, hi: 6 };
15 | println!("两头同时取:{:?} {:?} {:?} {:?}", c.next(), c.next_back(), c.next(), c.next_back());
16 | }
编译:成功
运行(退出码 0):
rev:[4, 3, 2, 1, 0]
len:7,size_hint:(7, Some(7))
两头同时取:Some(0) Some(5) Some(1) Some(4) 源码:
1 | struct NoHint(u32); struct WithHint(u32);
2 | impl Iterator for NoHint { type Item = u32; fn next(&mut self) -> Option<u32> { if self.0 == 0 { None } else { self.0 -= 1; Some(self.0) } } }
3 | impl Iterator for WithHint { type Item = u32; fn next(&mut self) -> Option<u32> { if self.0 == 0 { None } else { self.0 -= 1; Some(self.0) } } fn size_hint(&self) -> (usize, Option<usize>) { (self.0 as usize, Some(self.0 as usize)) } }
4 | fn main() {
5 | let a: Vec<u32> = NoHint(1000).collect(); let b: Vec<u32> = WithHint(1000).collect();
6 | println!("没有 size_hint:collect 之后 len = {},capacity = {}", a.len(), a.capacity());
7 | println!("有 size_hint :collect 之后 len = {},capacity = {}", b.len(), b.capacity());
8 | }
编译:成功
运行(退出码 0):
没有 size_hint:collect 之后 len = 1000,capacity = 1024
有 size_hint :collect 之后 len = 1000,capacity = 1000让自己的集合也能被 for 遍历,见IntoIterator 的三种遍历方式。