【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::Range1..10端点必须是可以迭代的整数类型。范围包含起始值,不包含终止值。
std::ops::RangeFrom1..无限定迭代。起始值必须时整数。如果值超过类型限制,可能会诧异或溢出
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, &strs.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::Receiverrecv.iter()产生另一个线程中对应 Sender 发送的值
std::io::Readstream.bytes()从 IO 流产生字节
stream.chars()将流作为 UTF-8 解析并产生字符
std::io::BufReadbufstream.lines()将流作为 UTF-8 解析并产生 String
bufstream.split(0)以给定字节分割流,产生该字节间的 Vec<u8> 缓冲
std::fs::ReadDirstd::fs::read_dir(path)产生目录项
std::net::TcpListenerlistener.incoming()产生到来的网络连接
Free functionsstd::iter::empty()立即返回 None
std::iter::once(5)产生给定的值,然后结束
std::iter::repeat("#9")一直产生给定的值

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

phial03

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值