Rust collect() 浅析

.collect() 浅析

rust 复制代码
let json_movies: Vec<_> = movies.iter().map(|m| m.to_json()).collect();

.collect() 是 Rust 迭代器的终端操作 (consuming adaptor),它把惰性迭代器"消费"掉,将沿途产生的所有元素收集到一个具体的集合类型中。


一、类比理解

复制代码
迭代器(Iterator)  ≈  工厂流水线
  .iter()            ≈  启动传送带
  .map(...)          ≈  每个零件经过加工工位(还未实际生产)
  .collect()         ≈  最终打包成箱子,触发整条流水线运转

关键点 :在 .collect() 被调用之前,整条迭代器链都是惰性的,不会执行任何实际操作。


二、逐层拆解

rust 复制代码
let movies = dm.get_movies_by_genre(&query.genre, size, sort_by);  // ① Vec<&Movie>
let json_movies: Vec<_> = movies
    .iter()                           // ② 创建迭代器,元素类型 &Movie
    .map(|m| m.to_json())            // ③ 对每个 &Movie 调用 to_json() → MovieJson
    .collect();                       // ④ 把所有 MovieJson 收集到 Vec<MovieJson>

movies --- 原始数据

rust 复制代码
// get_movies_by_genre 返回:
Vec<&Movie> = [
    &Movie { movie_id: 1, title: "Toy Story", ... },
    &Movie { movie_id: 2, title: "Jumanji", ... },
    &Movie { movie_id: 3, title: "Grumpier Old Men", ... },
]

.iter() --- 创建迭代器

rust 复制代码
// .iter() 返回一个 Iter<&Movie>,它实现了 Iterator trait
// 类型:impl Iterator<Item = &&Movie>

// 等价于手写:
let mut iter = movies.iter();  // 此时什么都没做,只是一个"蓝图"

.map(|m| m.to_json()) --- 转换元素

rust 复制代码
// .map() 返回另一个迭代器 Map<Iter<&Movie>, closure>
// 类型:impl Iterator<Item = MovieJson>

// 此时仍然什么都没做!map 只是记录了"当需要下一个元素时,执行这个闭包"

.collect() --- 触发执行 & 收集结果

rust 复制代码
// .collect() 遍历迭代器,对每个元素执行 map 闭包,
// 把结果 push 进一个新 Vec,返回这个 Vec
// 类型:Vec<MovieJson>

// 等价于手写:
let mut json_movies = Vec::new();
for m in movies.iter() {
    json_movies.push(m.to_json());
}

三、详细例子

输入数据

rust 复制代码
// 假设 dm.get_movies_by_genre("Comedy", 3, "rating") 返回:
movies = [
    &Movie { movie_id: 1, title: "Toy Story", ... },
    &Movie { movie_id: 2, title: "Jumanji",   ... },
    &Movie { movie_id: 3, title: "Grumpier",  ... },
]

collect() 执行的内部过程

复制代码
循环第 1 轮:
  iter.next() → Some(&&Movie { movie_id: 1, ... })   ← .iter() 取出第1个
  |m| m.to_json()                                      ← map 闭包执行
  → MovieJson { movie_id: 1, title: "Toy Story", ... } ← to_json() 转换
  → vec.push(MovieJson { movie_id: 1, ... })           ← 推入 Vec

循环第 2 轮:
  iter.next() → Some(&&Movie { movie_id: 2, ... })
  → MovieJson { movie_id: 2, title: "Jumanji", ... }
  → vec.push(...)

循环第 3 轮:
  iter.next() → Some(&&Movie { movie_id: 3, ... })
  → MovieJson { movie_id: 3, title: "Grumpier", ... }
  → vec.push(...)

循环第 4 轮:
  iter.next() → None  ← 迭代器耗尽,停止

最终:vec = [
    MovieJson { movie_id: 1, ... },
    MovieJson { movie_id: 2, ... },
    MovieJson { movie_id: 3, ... },
]

最终 JSON 输出

json 复制代码
[
  { "movieId": 1, "title": "Toy Story", "releaseYear": 1995, ... },
  { "movieId": 2, "title": "Jumanji", "releaseYear": 1995, ... },
  { "movieId": 3, "title": "Grumpier Old Men", "releaseYear": 1995, ... }
]

四、Vec<_> 类型推导

rust 复制代码
let json_movies: Vec<_> = movies.iter().map(|m| m.to_json()).collect();
//               ^^^^^^^
//               Vec<_> 告诉编译器"我需要一个 Vec"
//               _ 表示"元素类型你自己推导"

编译器看到:

  1. movies.iter() → 元素是 &&Movie
  2. .map(|m| m.to_json())to_json() 返回 MovieJson
  3. .collect() → 收集进 Vec<_>_ 推导为 MovieJson

最终类型Vec<MovieJson>

等价写法

rust 复制代码
let json_movies: Vec<MovieJson> = movies.iter().map(|m| m.to_json()).collect();  // 显式类型
let json_movies = movies.iter().map(|m| m.to_json()).collect::<Vec<_>>();       // turbofish 语法

五、collect 可以收集到多种集合类型

rust 复制代码
let v: Vec<_>   = (0..5).collect();   // [0, 1, 2, 3, 4]
let h: HashSet<_> = (0..5).collect(); // {0, 1, 2, 3, 4} (无序)
let s: String   = ['h','e','l','l','o'].iter().collect(); // "hello"
let m: HashMap<_, _> = [("a",1), ("b",2)].iter().cloned().collect(); // {"a":1, "b":2}

同一行 .collect() 能产出 VecHashSetHashMap 等不同集合------完全由左边变量的类型注解决定。


六、为什么需要 collect()?

没有 collect 有 collect
迭代器是惰性的,数据还没实际生成 立即执行所有计算,产生具体集合
类型是 impl Iterator<Item=MovieJson> 类型是 Vec<MovieJson>
不能传给 HttpResponse::Ok().json() .json() 只接受具体的可序列化类型
rust 复制代码
// ❌ 错误:Iterator 本身不能被序列化
let iter = movies.iter().map(|m| m.to_json());
HttpResponse::Ok().json(iter);  // 编译失败!

// ✅ 正确:collect 把迭代器变成 Vec,Vec 可序列化
let vec: Vec<_> = movies.iter().map(|m| m.to_json()).collect();
HttpResponse::Ok().json(vec);   // 编译通过

七、完整数据流图

复制代码
DataManager
  │
  └─ get_movies_by_genre("Comedy", 3, "rating")
       │
       ▼
     Vec<&Movie>          ← 原始内部数据
       │
       │ .iter()
       ▼
     Iter<&Movie>         ← 惰性迭代器(蓝图)
       │
       │ .map(|m| m.to_json())
       ▼
     Map<Iter, fn>        ← 惰性迭代器(蓝图 + 转换逻辑)
       │
       │ .collect()  ←────── 触发执行!遍历所有元素
       ▼
     Vec<MovieJson>       ← 具体集合,可被 serde 序列化
       │
       │ HttpResponse::Ok().json(...)
       ▼
     HTTP Response Body:
     [{"movieId":1,...}, {"movieId":2,...}, {"movieId":3,...}]
相关推荐
卷无止境1 小时前
python进阶:类方法与静态方法的分野,classmethod 和 staticmethod 到底该怎么选
后端·python
Wang's Blog1 小时前
Go-Zero项目开发33: IM实时通信前后端对接与微服务基础设施实践
开发语言·微服务·golang·go-zero
AINative软件工程1 小时前
LLM 应用的分层可观测性工程实践:从 Span 到 Prompt Diff,三层 Trace 让 AI 系统真正可调试
后端·ai编程
卷无止境1 小时前
继承与多态:Python OOP里最容易被用错的两把梯子
后端·python
SelectDB技术团队1 小时前
丰巢日志平台 ELK 替代:Apache Doris / SelectDB 的技术能力与实践
开发语言·python
BetterFlow_CFD1 小时前
COMSOL声学仿真基础知识(二)
开发语言·算法·matlab
明月_清风1 小时前
📊 Tokenomics 入门:如何看懂一个项目的代币经济?
后端·web3
麻瓜老宋1 小时前
AI开发C语言应用按步走,表达式计算器calc的第十九步,注释、表达式分隔符、long double 精度
c语言·开发语言·atomcode
崖边看雾1 小时前
Python学习——函数
开发语言·windows·python·学习·pycharm