22

Iterator in Rust (1)

 3 years ago
source link: http://bean-li.github.io/Iterator-in-Rust/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前言

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, &[]);
}

##


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK