【Rust 笔记】13-迭代器(上)

13 - 迭代器

  • 迭代器:是可以产生一系列值的值,通常用循环来操作。

  • 示例:

    /// 返回前n位正整数的和,即第n个三角形数
    fn triangle(n: i32) -> i32 {
          
          
      let mut sum = 0;
      for i in 1..n+1 {
          
          
        sum += i;
      }
      sum
    }
    
    // 通过迭代器的flod方法实现上述函数
    fn triangle(n: i32) -> i32 {
          
          
      (1..n+1).fold(0, |sum, item| sum + item)
      // fold 取得1..n+1产生的每个值,把累加值和这个值传给闭包。
      // 然后闭包又返回新的累加值。
    }
    

13.1-IteratorIntoIterator 特型

  • Rust 迭代器:是任何实现 `std::iter::Iterator 特型的值。

    trait Iterator {
          
          
      type Item;
      fn next(&mut self) -> Option<Self::Item>;
      ... // 许多默认方法
    }
    
    • Item 是迭代器产生的值的类型。
    • next 方法要么返回 Some(v),其中 v 是迭代器的下一个值;要么返回 None,表示序列终止。
  • IntoIterator 迭代器:实现迭代某种类型。

    trait IntoIterator where Self::IntoIter::Item == Self::Item {
          
          
      type Item;
      type IntoIter: Iterator;
      fn into_iter(self) -> Self::IntoIter;
    }
    
    • Item 是迭代器所产生值的类型。
    • IntoIter 是迭代器值本身的类型。
    • 任何实现 IntoIterator 特型的类型,被称为可迭代类型(iterable)。
  • Rust 的循环可以实现迭代操作。如下所示迭代某个向量的元素:

    println!("There's: ");
    let v = vec!["antimony", "arsenic", "aluminum", "selenium"];
    
    for element in &v {
          
             // 迭代器
      println!("{}", element); // 消费者
    }
    
  • 每个循环本质上只是对调用 IntoIteratorIterator 方法的简写:

    let mut iterator = (&v).into_iter();
    while let Some(element) = iterator.next() {
          
          
      println!("{}", element);
    }
    
    • 循环利用 IntoIterator::into_iter 方法将其操作数 &v 转换为一个迭代器。
    • 然后重复调用 Iterator::next
    • 每次返回 Some(element),循环都执行其循环体。
    • 如果返回的是 None,则循环结束。
  • 迭代器产生的值叫迭代项(item)。

  • 接收迭代器产生的迭代项的代码叫消费者(consumer)。

13.2 - 创建迭代器

13.2.1-iteriter_mut 方法

  • 大多数集合类型的 iteriter_mut 方法,返回该类型的迭代器,产生每个迭代项的共享或可修改引用。

    • 如下所示迭代器的迭代项类型是 &i32
    • 每次调用 next 都会产生对下一个元素的引用,直到向量的末尾。
    // 代替循环实现迭代器
    let v = vec![4, 20, 12, 8, 6];
    let mut iterator = v.iter();
    assert_eq!(iterator.next(), Some(&4));
    ...
    
  • std::path::Pathiter 方法返回的迭代器每次会产生路径的一个组件:

    • 这个迭代器的迭代项的类型是 &std::ffi::OsStr
    • 是操作系统调用可以接受的、借用的字符串切片。
    use std::ffi::OsSter;
    use std::path::Path;
    
    let path = Path::new("C:/users/JimB/Downloads/Fedora.iso");
    let mut iterator = path.iter();
    assert_eq!(iterator.next(), Some(OsStr::new("C:")));
    assert_eq!(iterator.next(), Some(OsStr::new("users")));
    ...
    

13.2.2-IntoIterator 实现

  • 如前所述,如果类型实现了 IntoIterator,则可以调用它的 into_iter 方法,实现等同循环的效果。

    use std::collections::BTreeSet;
    
    let mut favorites = BTreeSet::new();
    
    favorites.insert("Lucy".to_string());
    favorites.insert("Lie No. 3".to_string());
    
    let mut it = favorites.into_iter();
    
    assert_eq!(it.next(), Some("Lie No. 3".to_string()));
    assert_eq!(it.next(), Some("Lucy".to_string()));
    assert_eq!(it.next(), None);
    
  • 大多数集合实现了多个 IntoIterator,分别用于共享引用、可修改引用和转移。

    • 对于集合的共享引用:into_iter 会返回产生迭代项共享引用的迭代器。
    • 对于集合的可修改引用:into_iter 会返回产生迭代项可修改引用的迭代器。
    • 对于集合的按值转移:into_iter 返回的迭代器会取得集合的所有权,并按值返回迭代器。此时,迭代器的所有权从结合转移到消费者,原始集合在这个过程中会被消费掉。
  • for 循环会对其操作数应用 IntoIterator::into_iter,所以下述 3 个实现,可以支持迭代集合的共享引用、可修改引用,以及消费集合并取得其元素的所有权:

    • 每种实现,都对应上面的一种 IntoIterator 实现。
    for element in &collection {
          
           ... }
    for element in &mut collection {
          
           ... }
    for element in collection {
          
           ... }
    
  • HashSetBTreeSetBinaryHeap 没有对可修改引用实现 IntoIterator

  • HashMapBTreeMap 会产生它们值的可修改引用,但只产生它们键的共享引用。

  • 切片实现了 IntoIterator 的共享引用和可修改引用。

  • IntoIterator 的共享引用和可修改引用的实现,等价于在引用值上调用 iteriter_mut 方法:

    • IntoIteratorfor 循环底层的基础;
    • 在不使用 for 循环时,it.iter() 要比 (&it).into_iter() 清晰。
  • 在泛型代码中,T: IntoIterator 绑定可以限制类型变量 T 为可迭代的类型。

    • T: IntoIterator<Item=U> 可以进一步要求迭代产生指定的类型 U

      // 接收任何可迭代类型,将它们通过{:?}格式,将值打印出来
      use std::fmt::Debug;
      
      fn dump<T, U>(t: T) where T: IntoIterator<Item=U>, U: Debug {
              
              
        for u in t {
              
              
          println!("{:?}", u);
        }
      }
      
    • 不能使用 iteriter_mut 来实现这个泛型函数,因为它们不是任何特型的方法。

13.2.3-drain 方法

  • drain 方法:以一个集合的可修改引用为参数,返回一个能把每个元素所有权传给消费者的迭代器。

    • 只是借用对集合的引用。
    • 在迭代器被清除后,它会清空集合中所有剩余的元素。
  • 在可通过范围指定索引的类型,如 String、向量和 VecDeque 中,drain 方法接收要移除元素的范围,而不是排取(drain)整个序列:

    use std::iter::FromIterator;
    
    let mut outer = "Eearth".to_string();
    let inner = String::from_iter(outer.drain(1..4));
    
    assert_eq!(outer, "Eh");
    assert_eq!(inner, "art");
    
  • 如果确实要排取整个序列,那么可以使用全范围(..)作为参数。

13.2.4 - 其他迭代器源

类型或特型 表达式 说明
std::ops::Range 1..10 端点必须是可以迭代的整数类型。范围包含起始值,不包含终止值。
std::ops::RangeFrom 1.. 无限定迭代。起始值必须时整数。如果值超过类型限制,可能会诧异或溢出
Option<T> Some(10).iter() 类似长度为 0(None)或 1(Some(v))的向量
Result<T, E> Ok("blah").iter() 类似 Option,产生 Ok
Vec<T>, &[T] v.windows(16) 从左到右产生给定长度的每个连续切片。窗口重叠
v.chunks(16) 从左到右产生给定长度的非重叠连续切片
v.chunks_mut(1024) 类似 chunks,但切片是可修改的
`v.split( byte
v.split_mut(...) 同上,但产生可修改切片
v.rsplit(...) 类似 split,但从右到左产生切片
v.splitn(n, ...) 类似 split,但最多产生 n 个切片
String, &str s.bytes() 产生 UTF-8 形式的字节
s.chars() 产生 UTF-8 表示的字符
s.split_whitespace() 以空格分割字符串,产生非空格字符的切片
s.lines() 产生字符串行的切片
s.split('/') 以给定模式分割字符串,产生匹配之间内容的切片。模式可以是字符、字符串、闭包等
s.matches(char::is_numeric) 产生匹配给定模式的切片
std::collections::HashMap
std::collections::BTreeMap
map.keys(), map.values() 产生对映射键或值的共享引用
map.values_mut() 产生对条目值的可修改引用
std::collections::HashSet
std::collections::BTreeSet
set1.union(set2) 产生对 set1set2 合集元素的共享引用
set1.intersection(set2) 产生对 set1set2 交集元素的共享引用
std::sync::mpsc::Receiver recv.iter() 产生另一个线程中对应 Sender 发送的值
std::io::Read stream.bytes() 从 IO 流产生字节
stream.chars() 将流作为 UTF-8 解析并产生字符
std::io::BufRead bufstream.lines() 将流作为 UTF-8 解析并产生 String
bufstream.split(0) 以给定字节分割流,产生该字节间的 Vec<u8> 缓冲
std::fs::ReadDir std::fs::read_dir(path) 产生目录项
std::net::TcpListener listener.incoming() 产生到来的网络连接
Free functions std::iter::empty() 立即返回 None
std::iter::once(5) 产生给定的值,然后结束
std::iter::repeat("#9") 一直产生给定的值

详见《Rust 程序设计》(吉姆 - 布兰迪、贾森 - 奥伦多夫著,李松峰译)第十五章
原文地址

猜你喜欢

转载自blog.csdn.net/feiyanaffection/article/details/125574862