Iterator in Rust (1)

| 分类 Rust  | 标签 Rust 

前言

Iterators是迭代器,迭代器用来产生一串值。Rust提供了迭代器来遍历vectors,string,hashtables等,除此外:

  • 产生文本中的每一行的内容

  • 到达网络服务器的每个网络连接 (connection)

  • 通过communication channel获得的来自其他线程的值

我们也可以创建自己的迭代器。Rust的for 循环可以使用迭代器,除此外,迭代器本身也提供很丰富的方法来使用迭代器。

迭代器是弹性灵活的,表达力很强,而且效率很高。我们以下面的函数为例:

fn triangle(n: i32)-> i32{
    let mut sum = 0;
    for i in 1..n+1 {
        sum += i
    }
    sum
}

给定入参n ,计算, 1+2+ …+n 的值。函数写的啰嗦。如果此处使用迭代器,因为迭代器有fold方法,可以用如下语句简洁地完成同样的功能:

fn triangle_v2(n: i32) -> i32 {
    (1..n+1).fold(0, |sum, item| sum+item)
}

Iterator and IntoIterator Traits

实现了std::iter::Iterator的任意数据结构,都可以称之为迭代器:

trait Iterator {
	type Item ;
	fn next(&mut self) -> Option<self::Item> ;
}

Item是迭代器产生的数据类型。该Trait要实现一个next method来产生出Some(v):

  • 要么产生出该迭代器的next value
  • 要么返回None,表示迭代器已经迭代到尽头,无法产生新的value

如果某种数据结构,存在一种自然的迭代方法,这表明,该数据结构实现了std::iter::IntoIterator Trait,该trait中提供了into_iter方法处理此事,即:

  • 输入是该数据结构本身
  • 输出是该数据结构对应的迭代器

最典型最容易理解的是vector。

fn main() {
    println!("There's:");
    let v = vec!["antimony", "arsenic", "aluminum", "tony"];

    for elem in &v {
        println!("{}", elem);
    }

    println!("===========================================");
    let mut iterator = (&v).into_iter();
    while let Some(element) = iterator.next(){
        println!("{}", element);
    }
    println!("Hello, world!");
}

输出如下:

There's:
antimony
arsenic
aluminum
tony
===========================================
antimony
arsenic
aluminum
tony
Hello, world!

for循环使用IntoIterator::into_inter 把 &v转成了一个迭代器,然后不断地执行 Iterator::next。当Iterator::next 返回Some(element)时,执行循环体的内容,如果Iterator::next 返回None,那么循环结束。

上面示范代码中,两种迭代的方式本质时一样的,上面方法for循环的本质就是下面的方法。

如果返回Iterator::next 返回None之后,继续调用next方法会发生什么?Iterator未定义这种行为,大部分迭代器只是会再次返回None,但也不是全部的迭代器都是如此。

创建迭代器

iter 和 iter_mut 方法

大多数的collection类型提供了iter和iter_mut方法,可以返回迭代器。切片如&[T]或者&str 也有iter和iter_mut方法。这种方法比较通用和自然的方法产生迭代器。

fn main() {
    let v = vec!["antimony", "arsenic", "aluminum", "tony"];
    let mut iterator = v.iter();
    assert_eq!(iterator.next(), Some(&"antimony"));
    assert_eq!(iterator.next(), Some(&"arsenic"));
    assert_eq!(iterator.next(), Some(&"aluminum"));
    assert_eq!(iterator.next(), Some(&"tony"));
}

std::path::Path 也实现了iter方法,每次产生一个路径部分。

use std::ffi::OsStr ;
use std::path::Path ;

fn main() {
    let path = Path::new("/etc/ceph/ceph.conf");
    let mut iterator = path.iter();

    assert_eq!(iterator.next(), Some(OsStr::new("/")));
    assert_eq!(iterator.next(), Some(OsStr::new("etc")));
    assert_eq!(iterator.next(), Some(OsStr::new("ceph")));
    assert_eq!(iterator.next(), Some(OsStr::new("ceph.conf")));
}

IntoIterator 实现

如果一个类型实现了IntoIterator,那么就可以通过调用 into_iter方法,转换成迭代器。

use std::collections::BTreeSet ;

fn main() {
    let mut favorite = BTreeSet::new();
    favorite.insert("Rust Programming".to_string());
    favorite.insert("Hello World".to_string());
    favorite.insert("ZFS internal".to_string());

    let mut it = favorite.into_iter();
    assert_eq!(it.next(), Some("Hello World".to_string()));
    assert_eq!(it.next(), Some("Rust Programming".to_string()));
    assert_eq!(it.next(), Some("ZFS internal".to_string()));
    assert_eq!(it.next(), None);
}

上面的BTreeSet类型实现了IntoIterator trait,所以可以调用into_iter方法。BTreeSet中的元素,以升序迭代,因此,顺序是:

  • Hello World
  • Rust Programming
  • ZFS internal

实际上,大部分的collections类型都实现了IntoIterator trait。

我们需要注意如下三种遍历方法:

for element in &collection {..}
for element in &mut collection {..}
for element in collection {..}

第一种是共享引用,第二种是可变引用,第三种是会consume collection,即会获得元素的所有权。

但是我们也要小心,不是所有的collection都提供三种实现,比如HashSet和BTreeSet,不支持可变引用。

drain 方法

很多collection提供了drain方法。这个方法对该类型使用可变应用 mutable reference,即执行完drain之后,原始的类型发生了变化。调用该方法后:

  • 返回一个新的迭代器
  • 原迭代器的内容发生了变化
use std::iter::FromIterator;

fn main() {
    let mut outer = "Earth".to_string();
    let inner = String::from_iter(outer.drain(1..4));

    assert_eq!(outer, "Eh");
    assert_eq!(inner, "art");

    let mut v = vec![1,2,3];
    let u: Vec<_> = v.drain(1..).collect();
    assert_eq!(v , &[1]);
    assert_eq!(u, &[2,3]);

    v.drain(..);
    assert_eq!(v, &[]);
}

##


上一篇     下一篇