bigywater.tech
Rust · 68

for 循环如何转换成 Iterator

Rust 没有传统的 for(;;),for 只是语法糖。这篇用编译器的 HIR 输出直接看它被展开成什么,再手写同样的 match + loop 验证行为一致,用带打印的自定义迭代器数调用次数。

1 次
for 循环中 into_iter 的调用次数
4 次
3 个元素的迭代器上 next 的调用次数(最后一次是 None)
E0502
遍历 &v 时往 v 里 push

编译器的展开

实测 · 本机执行输出
  源码:
    pub fn total(v: &[i32]) -> i32 {
        let mut s = 0;
        for x in v { s += x; }
        s
    }
  -Z unpretty=hir 输出(去掉前面的 prelude 行):
    #[attr = PreludeImport]
    use std::prelude::rust_2024::*;
    fn total(v: &'_ [i32])
        ->
            i32 {
        let mut s = 0;
        {
            let _t =
                match into_iter(v) {
                    mut iter =>
                        loop {
                            match next(&mut iter) {
                                None {} => break,
                                Some {  0: x } => { s += x; }
                            }
                        },
                };
            _t
        };
        s
    }

HIR 展开使用了 RUSTC_BOOTSTRAP=1 与 -Z unpretty=hir(稳定版编译器上打开 nightly 选项),输出格式随编译器版本变化。三种遍历写法见IntoIterator 的三种遍历方式。

手写展开

实测 · 本机执行输出
  源码:
      1 | fn main() {
      2 |     let v = vec![1, 2, 3, 4, 5, 6];
      3 |     let mut a = Vec::new();
      4 |     for x in &v { if x % 2 == 0 { continue; } if *x > 4 { break; } a.push(*x); }
      5 |     let mut b = Vec::new();
      6 |     match IntoIterator::into_iter(&v) {
      7 |         mut iter => loop {
      8 |             match Iterator::next(&mut iter) {
      9 |                 None => break,
     10 |                 Some(x) => { if x % 2 == 0 { continue; } if *x > 4 { break; } b.push(*x); }
     11 |             }
     12 |         }
     13 |     }
     14 |     println!("for: {:?}   手写展开: {:?}   相同: {}", a, b, a == b);
     15 | }
  编译:成功
  运行(退出码 0):
    for: [1, 3]   手写展开: [1, 3]   相同: true

调用顺序

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
      into_iter() 被调用
      next() 被调用,n = 0
    循环体 x = 1
      next() 被调用,n = 1
    循环体 x = 2
      next() 被调用,n = 2
    循环体 x = 3
      next() 被调用,n = 3
    循环结束

自定义迭代器见自定义 Iterator。

遍历时的限制

实测 · 本机执行输出
  源码:
      1 | fn main() {
      2 |     let mut v = vec![1, 2, 3];
      3 |     for x in &v { if *x == 2 { v.push(4); } }
      4 | }
  编译:失败
    error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
     --> <实验目录>/src/u68d.rs:3:32
      |
    3 |     for x in &v { if *x == 2 { v.push(4); } }
      |              --                ^^^^^^^^^ mutable borrow occurs here
      |              |
      |              immutable borrow occurs here
      |              immutable borrow later used here
    
    error: aborting due to 1 previous error
    
    For more information about this error, try `rustc --explain E0502`.
  编译:成功
  运行(退出码 0):
    下标写法可以改: [1, 20, 3, 40]
    iter_mut 写法: [2, 21, 4, 41]
    0..n 循环 3 次
    循环体里把 m 改成 100 之后,仍然只循环 3 次

分步演示

for 循环展开
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动。HIR 用 RUSTC_BOOTSTRAP=1 -Z unpretty=hir
    • 没有验证:for 在 async 里的展开、for 对 break 带标签的展开、MIR 层的形态(只看到了 HIR)、HIR 里 into_iter/next 的完整路径(输出里被省略成短名)
    京ICP备2026067502号-1京公网安备11010502063487号