发布于2026-07-29 阅读(0)
扫一扫,手机访问
我们先看一段很常见的 Rust 代码:
let json_movies: Vec<_> = movies.iter().map(|m| m.to_json()).collect();
.collect() 是 Rust 迭代器里一个终端操作(consuming adaptor),它的作用说白了就是——把那条懒洋洋的迭代器链“消费”掉,让沿途产生的所有元素乖乖地收集到一个具体的集合类型里。没有它,整条链子就只是纸上谈兵。
迭代器(Iterator) ≈ 工厂流水线 .iter() ≈ 启动传送带 .map(...) ≈ 每个零件经过加工工位(还未实际生产) .collect() ≈ 最终打包成箱子,触发整条流水线运转
关键点:在 .collect() 被调用之前,整条迭代器链都是惰性的,不会执行任何实际操作。这一点太重要了,很多初学者在这里翻车。
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
// 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() 返回一个 Iter<&Movie>,它实现了 Iterator trait // 类型:impl Iterator- // 等价于手写: let mut iter = movies.iter(); // 此时什么都没做,只是一个"蓝图"
// .map() 返回另一个迭代器 Map, closure> // 类型:impl Iterator - // 此时仍然什么都没做!map 只是记录了"当需要下一个元素时,执行这个闭包"
// .collect() 遍历迭代器,对每个元素执行 map 闭包, // 把结果 push 进一个新 Vec,返回这个 Vec // 类型:Vec// 等价于手写: let mut json_movies = Vec::new(); for m in movies.iter() { json_movies.push(m.to_json()); }
// 假设 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", ... },
]
循环第 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, ... },
]
[
{ "movieId": 1, "title": "Toy Story", "releaseYear": 1995, ... },
{ "movieId": 2, "title": "Jumanji", "releaseYear": 1995, ... },
{ "movieId": 3, "title": "Grumpier Old Men", "releaseYear": 1995, ... }
]
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
let json_movies: Vec= movies.iter().map(|m| m.to_json()).collect(); // 显式类型 let json_movies = movies.iter().map(|m| m.to_json()).collect:: >(); // turbofish 语法
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 等不同集合——完全由左边变量的类型注解决定。这一点在设计 API 时特别有用,你可以根据上下文灵活切换容器。
| 没有 collect | 有 collect |
|---|---|
| 迭代器是惰性的,数据还没实际生成 | 立即执行所有计算,产生具体集合 |
类型是 impl Iterator | 类型是 Vec |
不能传给 HttpResponse::Ok().json() | .json() 只接受具体的可序列化类型 |
// ❌ 错误: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 ← 惰性迭代器(蓝图 + 转换逻辑)
│
│ .collect() ←────── 触发执行!遍历所有元素
▼
Vec ← 具体集合,可被 serde 序列化
│
│ HttpResponse::Ok().json(...)
▼
HTTP Response Body:
[{"movieId":1,...}, {"movieId":2,...}, {"movieId":3,...}]
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8