bigywater.tech
Rust · 66

peekable、inspect 与调试

写解析器经常需要「看一眼下一个元素再决定」,调试迭代器链又需要「看一眼中间的值」。Peekable 和 inspect 分别解决这两件事。这篇用计数器验证 peek 的真实行为,用词法分析对比 take_while,并用 inspect 看清链的执行顺序。

1 个
peek 一次后源已产出的元素数(创建时是 0)
E0499
持有 peek 返回的引用时再 next
E0594
在 inspect 里修改元素

peek 的真实行为

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    刚创建:源已产出 0 个
    peek -> Some(1),源已产出 1 个
    再 peek -> Some(1),源已产出 1 个(没有再拉)
    next -> Some(1),源已产出 1 个(返回的是缓存的 1)
    next -> Some(2),源已产出 2 个
    peek_mut 改成 99: Some(99)
    size_of Peekable<Range<i32>> = 16 字节(Range 本身 8)

peek 的借用

实测 · 本机执行输出
  源码:
      1 | fn main() {
      2 |     let mut it = [1, 2, 3].iter().peekable();
      3 |     let first = it.peek().unwrap();
      4 |     it.next();
      5 |     println!("{}", first);
      6 | }
  编译:失败
    error[E0499]: cannot borrow `it` as mutable more than once at a time
     --> <实验目录>/src/u66b.rs:4:5
      |
    3 |     let first = it.peek().unwrap();
      |                 -- first mutable borrow occurs here
    4 |     it.next();
      |     ^^ second mutable borrow occurs here
    5 |     println!("{}", first);
      |                    ----- first borrow later used here
    
    error: aborting due to 1 previous error
    
    For more information about this error, try `rustc --explain E0499`.

词法分析

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    peekable + next_if: ["12", "+", "345", "*", "6"]
    take_while 读第一个数字 "12",剩余 "345*6"(分隔符 '+' 已经被丢掉)
    next_if_eq(&'a') -> Some('a')  next_if_eq(&'z') -> None  剩余 "b"

iterator 的惰性见Iterator 的惰性执行。

inspect

实测 · 本机执行输出
  源码:
      1 | fn main() {
      2 |     let r: Vec<i32> = (1..=5)
      3 |         .inspect(|x| println!("  [源] {}", x))
      4 |         .filter(|x| x % 2 == 1)
      5 |         .inspect(|x| println!("  [filter 之后] {}", x))
      6 |         .map(|x| x * x)
      7 |         .inspect(|x| println!("  [map 之后] {}", x))
      8 |         .collect();
      9 |     println!("结果 {:?}", r);
     10 |     let s: i32 = [1, 2, 3].iter().inspect(|x| print!("{} ", x)).sum();
     11 |     println!("sum = {}(inspect 的参数是 &Item,不能修改元素)", s);
     12 | }
  编译:成功
  运行(退出码 0):
      [源] 1
      [filter 之后] 1
      [map 之后] 1
      [源] 2
      [源] 3
      [filter 之后] 3
      [map 之后] 9
      [源] 4
      [源] 5
      [filter 之后] 5
      [map 之后] 25
    结果 [1, 9, 25]
    1 2 3 sum = 6(inspect 的参数是 &Item,不能修改元素)
  源码:
      1 | fn main() {
      2 |     let v: Vec<i32> = [1, 2, 3].iter().inspect(|x| **x += 1).copied().collect();
      3 |     println!("{:?}", v);
      4 | }
  编译:失败
    error[E0594]: cannot assign to `**x`, which is behind a `&` reference
     --> <实验目录>/src/u66e.rs:2:52
      |
    2 |     let v: Vec<i32> = [1, 2, 3].iter().inspect(|x| **x += 1).copied().collect();
      |                                                    ^^^^^^^^ cannot assign
    
    error: aborting due to 1 previous error
    
    For more information about this error, try `rustc --explain E0594`.

dbg! 宏本身见dbg! 宏的实现方式。

分步演示

peekable / inspect
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动
    • 没有验证:Peekable 对 DoubleEndedIterator(next_back)的行为、peek 在带副作用的源上的更多影响、dbg! 与 inspect 搭配的输出差异
    京ICP备2026067502号-1京公网安备11010502063487号