—— 2022/9/5
一、 参考来源
二、迭代器 Iterator
凡是实现了 std::iter::Iterator 这个trait的对象,都可以称为迭代器。而迭代器的一个关键方法是next()。当然,trait里还提供了一些自定义的功能,详细可查文档。
三、迭代器协议
这里提供三种常见的方法来创建迭代器。
iter() 它在 &T 上进行迭代
iter_mut() 它在 &mut T 上进行迭代
into_iter() 它在 T 上进行迭代
文档地址。
这里的三个方法签名是迭代器协议,也就是说只要你符合使用规范,一般使用都可以进行迭代操作。
迭代器协议规范:
pub fn iter(&self) -------------------> Iterator ---------------> &Item;
pub fn iter_mut(&mut self) ------> Iterator ---------------> &mut Item;
pub fn into_iter(self) ---------------> Iterator ---------------> Item;
从标准库里,只定义了into_iter() 这个方法的接口—— trait IntoIterator
四、转换迭代的接口 IntoIterator
其参考文档为 IntoIterator
这个trait的含义是,当实现了这个trait的对象,其自身可通过方法into_iter(self) 转换为一个迭代器。注意,对象T的所有权会转移到这个方法里的,看方法里的是self。
五、for 循环
std::iter 的文档中提到for循环语法其实是一个迭代器的语法糖。
反糖化近似为,
六、解决 for 会转移对象所有权的解决办法
迭代器协议中 into_iter 的已经实现了,还有两个规定了不会转移所有权。那么这种在for中是怎样被解决的呢?
文档 中有提到,两种约定用法。
这里,如果我们查看 Vec 的 文档 便可以发现,Vec 的 iter() 是在左栏中的。
这说明了,其自身没有实现这个方法,但因为其实现了 trait Deref ,并返回的是 [T] 切片类型,所以其就是一个关于 [T] 切片的智能指针,同时继承了 [T] 的所有方法。
那么我们切换到 slice 的文档。
同样,我们能在左栏中找到 iter() 和 iter_mut() 这两个方法。
从上图的名字我们可以得出,iter() 和 iter_mut() 是其自身实现,并没有相应的接口。故可以推出迭代器的这两项是我们自身定义的,只是约定俗成。不过为了阅读方便,最好在实现时,语义统一。
七、for 循环中关于引用类型的妙用
文档中,对于for循环怎样实现不转移所有权,介绍了一个小妙招。
对for反糖后,大概得,
在Rust中,引用也是一种类型,也可以实现 trait。故我们可以利用好这点,为类型的引用类型实现 IntoIterator。例如,
当然为了与迭代器协议的语义一致,我们要设计一个实现了 Iterator 的迭代器里面的 next 返回的是 Item 的引用。这里就可以参考 slice 里的 iter() 和 iter_mut() 的实现了。
请注意,&T 与 &mut T 是两种不同的类型。这样的区分,有利于那些规定了不能修改内容的对象,例如python的tuple类型。
八、迭代器例子
接下来,就让我们创建一个自己的迭代器。这例子是模仿 Vec 和 slice 来写的,目的是与上面约定的迭代器协议一致。
首先我们创建一个 tuple structure。
迭代的目的是使用for循环从第一个数迭代到第三个数。
迭代器协议把迭代对象和数据对象分开了,同时也分成了三种类型。
- 第一种类型是转移所有权的方式,转换器是 into_iter(&self),
首先创建一个IterA,用于接收类型A的实例对象。换言之,其就是在for循环内主要实现迭代功能的对象。
那么还要做一个A与IterA 的转换器。
OK,第一种类型完成。
- 第二种是使用不可变引用,转换器是 iter(&self)。
因为iter() 方法不是trait里定义的,所以其不是一定实现。但是为了语义一致,且参考了slice里的设计,这里加进去。
接下来,如上,首先设计一个迭代器,接收的是&self类型。
然后再实现A与IterRefA的转换器。
- 第三种是使用可变引用,转换器是iter_mut(&mut self)
其代码与第二种相近。这里省略不表。
请注意的是,可变的可以不实现,其意味着,你不想提供一种可变的方式来迭代。类似于&’static str这种,不想让用户修改其内容。故可不提供实现。
综上所述,要实现一个迭代器,一般是三个步骤。
设计数据;
包装数据;
实现转换;
九、IntoIterator与Iterato有趣关系
在上面说过,for循环是先调用对象的into_iter方法。当一个对象直接实现了Iterator,但没有自定义IntorIterator时,for循环也成功执行。这是因为,在IntoIterator里,做了一个这样的实现,
其意思是,为所有实现了Iterator的对象,同样也实现一份IntoIterator。这是一个很有趣的说明,一个Iterator对象自身也能转换为一个Iterator对象,即其自身。
开始听时有点懵圏,但对比了Python,你会发现比比皆是,这种现象。