.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"
// _ 表示"元素类型你自己推导"
编译器看到:
movies.iter()→ 元素是&&Movie.map(|m| m.to_json())→to_json()返回MovieJson.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() 能产出 Vec、HashSet、HashMap 等不同集合------完全由左边变量的类型注解决定。
六、为什么需要 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,...}]