rust 里面的 for 循环实际上是个迭代器的语法糖。
比如常见的
1 2 3
| for i in 1..100 { todo!(); }
|
这里我们用 let 绑定一个变量到 1..100 上,利用标准库里的 type_name::() 查看类型信息。
1 2 3 4 5 6 7 8 9
| fn type_of<T>(_: &T) -> &'static str{ std::any::type_name::<T>() }
fn main() { let i = 1..10; println!("{}", type_of(&i)); }
|
编译运行终端的输出为
而标准库中这个泛型类型也实现了与迭代器相关的 trait
在实际编译时 for 循环会被展开成常规的循环。
也正是因为 for 循环的这种特性,任何一个集合类型只要实现了迭代器相关的 trait , 就可以遍历其所有子元素并进行操作。
首先我们来为我们的链表的 IntoIter 结构体实现 Iterator trait
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| pub struct IntoIter<T>(List<T>);
impl<T> List<T> { pub fn into_iter(self) -> IntoIter<T> { IntoIter(self) } }
impl<T> Iterator for IntoIter<T> { type Item = T; fn next(&mut self) -> Option<Self::Item> { self.0.pop() } }
|
这里我们首先实现了一个元组结构体 IntoIter ,这是一个泛型的结构体,其泛型参数与我们的链表保持一致,然后我们对其实现了 Iterator trait 。这个 trait 里面除了 next() 方法外还有一个关联类型 Item , 在这里我们将其定义为为我们链表的泛型参数。
next() 方法的返回值即为使用 Option 包裹的关联类型。
实现了此 trait 即可使用 into_iter() 方法,注意此方法会消费所有权,绝大多数情况下这种并不常见。
接下来我们为我们的链表实现不获取所有权的 Iter 结构体和对应的 Iterator trait 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| pub struct Iter<'a, T> { next: Option<&'a Node<T>>, }
impl<'a, T> Iterator for Iter<'a, T> { type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> { self.next.map(|node| { self.next = node.after.as_deref(); &node.elem }) } }
|
由于涉及到数据的借用,这里需要显示的标注出结构体的生命周期,这里满足生命周期省略原则,所以尽管 impl 块上标注了生命周期 'a ,但是 next() 方法并没有标出。
简单来说,在这里返回的借用的生命周期必然短于接受的借用,所以可以认为两者具有相同的生命周期约束 'a,其长短取决于返回借用实际存活的生命周期 。
类似的,返回拥有可变借用的 IterMut 结构体和 Iterator trait也是类似的设计。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| pub struct IterMut<'a, T> { next: Option<&'a mut Node<T>>, }
impl<'a, T> Iterator for IterMut<'a, T> { type Item = &'a mut T;
fn next(&mut self) -> Option<Self::Item> { self.next.take().map(|node| { self.next = node.after.as_deref_mut(); &mut node.elem }) } }
|
在剩下的两种迭代器的设计中,我们的代码用到了 as_deref() 和 as_deref_mut() 方法。
这两个方法的目的是将 Option 或其(可变)借用映射成用 Option 包装的包装物,以下为其对应的函数签名。
1 2 3
| pub const fn as_deref(&self) -> Option<&T::Target> where T: Deref,
|
1 2 3
| pub const fn as_deref_mut(&mut self) -> Option<&mut T::Target> where T: DerefMut,
|