bigywater.tech
Rust · 54

IntoIterator 的三种遍历方式

for x in v 与 for x in &v 看起来只差一个 &,含义却完全不同。这篇用元素类型的真实打印、所有权的编译报错、以及为自己的集合补齐三种 impl,把 IntoIterator 的三种遍历讲清楚。

3 个
为自己的集合实现 IntoIterator 需要的 impl 个数(T、&T、&mut T)
E0382
for x in v 之后再使用 v
E0277
没为 &Bag 实现 IntoIterator 时 for x in &bag 的错误

三种遍历

实测 · 本机执行输出
  源码:
      1 | fn tn<T>(_: &T) -> &'static str { std::any::type_name::<T>() }
      2 | fn main() {
      3 |     let mut v = vec![String::from("a"), String::from("b")];
      4 |     for x in &v { println!("for x in &v      → x 的类型 {}", tn(&x)); break; }
      5 |     for x in &mut v { println!("for x in &mut v  → x 的类型 {}", tn(&x)); x.push('!'); }
      6 |     for x in v.clone() { println!("for x in v      → x 的类型 {}", tn(&x)); break; }
      7 |     println!("修改之后 v = {:?}", v);
      8 | }
  编译:成功
  运行(退出码 0):
    for x in &v      → x 的类型 &alloc::string::String
    for x in &mut v  → x 的类型 &mut alloc::string::String
    for x in &mut v  → x 的类型 &mut alloc::string::String
    for x in v      → x 的类型 alloc::string::String
    修改之后 v = ["a!", "b!"]

for 循环的底层展开见for 循环如何转换成 Iterator。

for x in v 之后

实测 · 本机执行输出
  源码:
      1 | fn main() {
      2 |     let v = vec![1, 2, 3];
      3 |     for x in v { println!("{x}"); }
      4 |     println!("{:?}", v);
      5 | }
  编译:失败
    error[E0382]: borrow of moved value: `v`
       --> <实验目录>/src/u54b.rs:4:22
        |
      2 |     let v = vec![1, 2, 3];
        |         - move occurs because `v` has type `Vec<i32>`, which does not implement the `Copy` trait
      3 |     for x in v { println!("{x}"); }
        |              - `v` moved due to this implicit call to `.into_iter()`
      4 |     println!("{:?}", v);
        |                      ^ value borrowed here after move
        |
    note: `into_iter` takes ownership of the receiver `self`, which moves `v`
       --> <rust 标准库源码>/library/core/src/iter/traits/collect.rs:312:18
        |
    312 |     fn into_iter(self) -> Self::IntoIter;
        |                  ^^^^
    help: consider iterating over a slice of the `Vec<i32>`'s content to avoid moving into the `for` loop
        |
      3 |     for x in &v { println!("{x}"); }
        |              +
    
    error: aborting due to 1 previous error
    
    For more information about this error, try `rustc --explain E0382`.

自己的集合

实测 · 本机执行输出
  源码:
      1 | struct Bag { items: Vec<i32> }
      2 | impl IntoIterator for Bag { type Item = i32; type IntoIter = std::vec::IntoIter<i32>; fn into_iter(self) -> Self::IntoIter { self.items.into_iter() } }
      3 | impl<'a> IntoIterator for &'a Bag { type Item = &'a i32; type IntoIter = std::slice::Iter<'a, i32>; fn into_iter(self) -> Self::IntoIter { self.items.iter() } }
      4 | impl<'a> IntoIterator for &'a mut Bag { type Item = &'a mut i32; type IntoIter = std::slice::IterMut<'a, i32>; fn into_iter(self) -> Self::IntoIter { self.items.iter_mut() } }
      5 | fn main() {
      6 |     let mut bag = Bag { items: vec![1, 2, 3] };
      7 |     for x in &bag { print!("读 {x} "); }
      8 |     println!();
      9 |     for x in &mut bag { *x *= 10; }
     10 |     for x in &bag { print!("读 {x} "); }
     11 |     println!();
     12 |     let total: i32 = bag.into_iter().sum();           // 消费 bag
     13 |     println!("消费后求和 = {total}");
     14 | }
  编译:成功
  运行(退出码 0):
    读 1 读 2 读 3 
    读 10 读 20 读 30 
    消费后求和 = 60
  编译:失败
    error[E0277]: `&Bag` is not an iterator
     --> <实验目录>/src/u54d.rs:2:56
      |
    2 | fn main() { let bag = Bag { items: vec![1] }; for x in &bag { println!("{x}"); } }
      |                                                        ^^^^ `&Bag` is not an iterator
      |
      = help: the trait `Iterator` is not implemented for `&Bag`
      = note: required for `&Bag` to implement `IntoIterator`
    
    error: aborting due to 1 previous error
    
    For more information about this error, try `rustc --explain E0277`.

Iterator 本身见Iterator Trait。

其它集合

实测 · 本机执行输出
  源码:
      1 | use std::collections::BTreeMap;
      2 | fn tn<T>(_: &T) -> &'static str { std::any::type_name::<T>() }
      3 | fn main() {
      4 |     let arr = [String::from("x"), String::from("y")];
      5 |     for s in arr { println!("数组 for s in arr → {}", tn(&s)); break; }
      6 |     let mut m = BTreeMap::new(); m.insert("k", 1);
      7 |     for kv in &m { println!("for kv in &m      → {}", tn(&kv)); }
      8 |     for kv in m.clone() { println!("for kv in m       → {}", tn(&kv)); }
      9 |     for v in m.values_mut() { *v += 1; }
     10 |     println!("values_mut 之后 {:?}", m);
     11 | }
  编译:成功
  运行(退出码 0):
    数组 for s in arr → alloc::string::String
    for kv in &m      → (&&str, &i32)
    for kv in m       → (&str, i32)
    values_mut 之后 {"k": 2}

分步演示

IntoIterator
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动。类型名用 std::any::type_name 打印
    • 没有验证:iter() 与 (&v).into_iter() 是否完全等价(只验证了行为)、数组在 edition 2018 里的 into_iter 行为差别、drain、into_iter 对 Vec 的原地优化
    京ICP备2026067502号-1京公网安备11010502063487号