RUST 每日一省:迭代器2

创建迭代器

        1、大多数集合类型提供了 iter 和 iter_mut 方法,返回该类型的迭代器,产生每个迭代项的共享或可修改引用。切片类型 &[T] 和 &str 也有iter 和 iter_mut 方法。

let v = vec![4, 20, 12]; let mut iterator = v.iter();

assert_eq!(iterator.next(), Some(&4));

assert_eq!(iterator.next(), Some(&20));

 assert_eq!(iterator.next(), Some(&12));

 assert_eq!(iterator.next(), None);

        2、如果类型实现了 IntoIterator,则可以调用它的 into_iter 方法,跟for 循环一样。实际上大多数集合提供了不止一个 IntoIterator 的实现,分别用于共享引用、可修改引用和转移。

        并不是所有类型都会提供全部 3 种实现。例如,HashSet、BTreeSet 和BinaryHeap 就没有对可修改引用实现 IntoIterator,因为修改它们的元素可能违背类型的不变性,比如被修改的值变成不同的散列值或者相对周边元素做了不同排序,导致修改后把元素放到错误的位置上。其他类型确实有支持可修改引用的,但只是部分支持。例如,HashMap 和 BTreeMap会产生它们值的可修改引用,但只产生它们键的共享引用,原因与前面所讲类似

        切片实现了 3 种 IntoIterator 变体中的两个,因为它本身没有元素的所有权,所以就没有“按值”这种情况。而 &[T] 和 &mut [T] 的into_iter 返回的迭代器,可以产生对它们元素的共享引用和可修改引用。如果把底层切片类型 [T] 想象为某种类型的集合,就能从整体上理解这种实现了

        3、其他的如下,还有很多,大家自己自行去找找看。

std::ops::Range

Option(Some(10).iter())

Result (Ok("blah").iter())

String, &str  s.bytes()、s.chars()、s.lines()

迭代器消费

        Rust中的迭代器都是惰性的,它们不会自动发生遍历行为,必须调用next方法去消费其中的数据。所以在Iterator trait提供的默认方法中调用next的方法也被称为消耗适配器,因为它们同样消耗了迭代器本身。这也是我们需要在实现Iterator trait时手动定义next方法的原因。

        面介绍常用的消费器collect,其他消费器可以在std::iter::Iterator中找到。collect可以将迭代器转换成指定的容器类型,即将迭代器中的元素收集到指定的容器中。

fn main() {

 let v1 = [1, 2, 3, 4, 5];

 let v2: Vec = v1.iter().map(|x| x + 1).collect();

 

 println!("{:?}", v2);

 }

 // [2, 3, 4, 5, 6]

        通过iter方法将数组转换为迭代器,再使用map方法对原迭代器中的每个元素调用闭包执行加1操作并生成一个新迭代器,最后调用collect方法将新迭代器中的元素收集到动态数组中。

迭代器适配

        Iterator 提供大量可供选择的适配器方法,它会将当前迭代器转换成另一种类型的迭代器,并支持链式调用多个迭代器适配器。不过,由于所有的迭代器都是惰性的,必须使用一个消费器来获取迭代器适配器的调用结果。下面介绍常用的迭代器适配器map、take、filter等,其他迭代器适配器可以在std::iter中找到。

fn main() {

let v = vec![1,2,3,4,5,6,7,8,9];

let mut iter = v.iter()

.take(5)

.filter(|&x| x % 2 == 0) .map(|&x| x * x) .enumerate();

while let Some((i, v)) = iter.next() { println!("{} {}", i, v);

}

        通过take获取前5个数字,使用filter找到偶数,然后通过map实现平方,最后返回下标和数字本身。

自定义迭代器

        我们试一下如何实现一个迭代器。 假设我们的目标是, 这个迭代器会生成一个从1到10的序列。 我们创建一个struct,实现Iterator trait。 由于每次调用next方法的时候, 它都返回下一个值, 所以我们添加一个成员, 记录当前的状态。 代码如下

 

use std::iter::Iterator; 
struct Seq {

    current : i32

} 

impl Seq {

    fn new() -> Self {

    Seq { current: 0 }

    } 
} 

impl Iterator for Seq { 
    type Item = i32;

    fn next(&mut self) -> Option {

    if self.current < 10 { 
        self.current += 1;
        return Some(self.current); 
    } else {
        return None;
    }

    } 
} 
fn main() {

    let mut seq = Seq::new();

    while let Some(i) = seq.next() { 
        println!("{}", i);

    } 
}

        当然,for 循环要使用 IntoIterator::into_iter 将其操作数转换为一个迭代器。但标准库为每个实现 Iterator 的类型都提供了对IntoIterator 的通用实现,因此 Seq 已经可以使用了

        自定义类型也可以实现 IntoIterator 和 Iterator 特型,从而可以让本章介绍的所有适配器和消费者都派上用场,当然还包括使用标准迭代器接口的第三方库。

 

你可能感兴趣的:(rust,开发语言,后端)