bigywater.tech
Rust · 64

collect 类型推断

collect 看似简单,实际上是个泛型方法,返回什么类型全靠推断。这篇验证它什么时候报错、能收集成哪些类型、收集成 Result 时何时停止、同一份数据在不同类型里语义怎么变,以及 size_hint 如何影响 Vec 的预分配。

E0283
collect 没有目标类型的错误
3 / 5
收集成 Result 时,5 个元素里闭包被调用的次数
0 对 11
map 链与 filter 链收集 10000 个元素的 realloc 次数

目标类型与推断

实测 · 本机执行输出
  源码:
      1 | fn main() {
      2 |     let v = (1..4).map(|x| x * 2).collect();
      3 |     println!("{:?}", v);
      4 | }
  编译:失败
    error[E0283]: type annotations needed
        --> <实验目录>/src/u64a.rs:2:9
         |
       2 |     let v = (1..4).map(|x| x * 2).collect();
         |         ^                         ------- type must be known at this point
         |
         = note: the type must implement `FromIterator<i32>`
    note: required by a bound in `collect`
        --> <rust 标准库源码>/library/core/src/iter/traits/iterator.rs:2077:19
         |
    2077 |     fn collect<B: FromIterator<Self::Item>>(self) -> B
         |                   ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Iterator::collect`
    help: consider giving `v` an explicit type
         |
       2 |     let v: Vec<_> = (1..4).map(|x| x * 2).collect();
         |          ++++++++
    
    error: aborting due to 1 previous error
    
    For more information about this error, try `rustc --explain E0283`.
  编译:成功
  运行(退出码 0):
    标注变量类型 [1, 2, 3] / turbofish [1, 2, 3] / turbofish+占位符 [1, 4, 9] / 靠函数返回类型 [2, 4, 6]
    HashMap(排序后显示): [("a", 1), ("bb", 2), ("ccc", 3)]
    BTreeSet<char>: {'e', 'h', 'l', 'o'}
    String 可以从 char / &str / String 收集: rust rust ab
    Box<[i32]>: [1, 2, 3]
    partition: [2, 4, 6] [1, 3, 5]
    unzip: [1, 2] ['a', 'b']
    collect 成 (): Ok(())

Result 与 Option

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    Result<Vec<_>,_>: Err(ParseIntError { kind: InvalidDigit })  闭包只被调用了 3 次(共 5 个元素)
    全部成功: Some([1, 2, 3])  调用 3 次
    Option 的 sum: None / Some(4)

收集成不同类型

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    Vec 保留 3 个: [("a", 1), ("b", 2), ("a", 3)]
    BTreeMap 重复键后者覆盖前者,剩 2 个: {"a": 3, "b": 2}
    HashSet 只取键,剩 2 个
    HashMap 里 a = 3

HashMap 与 BTreeMap 的区别见BTreeMap 与 HashMap 对比。

size_hint 与预分配

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    map:    alloc 1 realloc 0  size_hint (10000, Some(10000))  len 10000 cap 10000
    filter: alloc 1 realloc 11  size_hint (0, Some(10000))  len 5000 cap 8192
    filter+map: alloc 1 realloc 11  len 5000 cap 8192

Vec 的扩容规则见Vec 扩容机制。

分步演示

collect
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动
    • 没有验证:自定义类型实现 FromIterator 的写法(在 Iterator 相关文章之外未涉及)、collect 成 Cow/Rc<[T]> 等较少见目标、size_hint 的更多适配器(chain、flat_map、take)、HashMap 收集时的预分配
    京ICP备2026067502号-1京公网安备11010502063487号