一句话总结
三个最常用的标准库集合:Vec(可增长数组,对应 Go slice),HashMap(键值映射,对应 Go map),String(可增长 UTF-8 字符串).
它们都在堆上分配,自动管理内存.Rust 在 API 上比 Go 更显式--查询返回 Option 而非零值,且区分 String 与 &str.
Vec:可增长的数组
Vec<T> 是堆上分配,可动态增长的序列,等价于 Go 的 []T:
|
访问元素有两种方式,体现 Rust 的"显式处理失败"哲学:
|
最佳实践:不可信索引用 get
索引来自用户输入,计算结果等不确定来源时,用 v.get(i) 把"可能越界"交给 Option 处理,而非裸用 v[i] 冒 panic 风险.
只有当确信索引合法时才用 [].这又是一次"用 Option 表达可能失败"(第 06 篇).
遍历 Vec 的三种方式
遍历时要留意所有权--直接呼应第 03,04 篇:
|
默认用 &v 遍历
大多数情况只是想读一遍,用 for x in &v 借用即可,别误用 for x in v--后者会消耗掉整个 Vec,循环后它就不能再用了.
这是新手常犯的"遍历后变量没了"困惑.
HashMap:键值映射
HashMap<K, V> 对应 Go 的 map[K]V,但 API 风格不同--查询返回 Option,从根上避免了"零值歧义":
|
Go 里 m["missing"] 返回零值,得用 v, ok := m[k] 区分"真的是 0"还是"不存在".
Rust 的 get 直接返回 Option:None 就是不存在,Some(0) 就是值为 0,语义天然无歧义,不会再有"零值陷阱".
entry API:优雅处理"有则更新,无则插入"
这是 HashMap 最该掌握的便利."统计词频""累加计数"这类需求,Go 要写 if _, ok := m[k]; !ok {...},Rust 用 entry 一行搞定:
|
entry(key) 返回该键的"入口",or_insert(default) 在键不存在时插入默认值,并统一返回值的可变引用供修改.比"先查再改"既简洁又少一次哈希查找.
String 与 &str
Rust 的字符串有两个核心类型,初学者必须分清:
| 类型 | 本质 | 类比 |
|---|---|---|
String |
拥有所有权,可增长,堆分配 | Go 里可变的字符串构建(strings.Builder 的角色) |
&str |
字符串切片,借用一段 UTF-8 数据,不拥有 | Go 的 string(只读视图) |
|
最佳实践回顾:参数用 &str
呼应第 04 篇:函数接收字符串参数时优先用 &str,它能同时接受 String 的引用和字面量,通用性最好.
只有当函数需要拥有或修改字符串时,才接收 String 或 &mut String.
UTF-8 带来的两个注意点
Rust 字符串严格按 UTF-8 存储:
- 不能用整数下标
s[0](回顾第 02 篇),要按字符用.chars(),按字节用.bytes() .len()返回的是字节数而非字符数
字符串的完整细节(还有 OsString,Cow 等)在进阶分类有专题.
容量与性能:with_capacity
Vec 和 String 增长时,若容量不足会重新分配内存并搬移数据.若能预估规模,用 with_capacity 预分配可避免多次重分配--这点与 Go 的 make([]T, 0, n) 思路完全一致:
|
最佳实践:已知规模就预分配
在循环里往 Vec/String 大量 push 且能预估数量时,用 with_capacity 预分配.这是低成本的性能优化,尤其在热路径上.
容量(capacity)与长度(len)是两个概念:len 是当前元素数,capacity 是已分配空间.
其他常用集合(了解即可)
| 集合 | 用途 |
|---|---|
HashSet<T> |
无序去重集合,判断元素是否存在 |
BTreeMap<K,V> |
按键有序的映射(Go 无内置对应,需自己排序) |
VecDeque<T> |
双端队列,头尾都能高效增删 |
快速回顾
- Vec:可增长数组;
[]越界 panic,get返回 Option(不可信索引用它). - 遍历:默认
for x in &v借用;for x in v会消耗集合. - HashMap:
get返回 Option,无零值歧义;entry().or_insert()优雅处理插入更新. - String vs &str:前者拥有可增长,后者是借用视图;参数优先
&str. - UTF-8:不能整数下标,
.len()是字节数,按字符用.chars(). - 预分配:已知规模用
with_capacity避免反复扩容;len 与 capacity 是两个概念.
动手练习
- 越界对比:用
vec!创建整数向量,分别用v[i]和v.get(i)访问一个越界索引,对比行为. - 词频统计:用
HashMap+entryAPI 统计一句话里每个单词出现的次数. - String 拼接:构造一个
String,用push_str/push拼接,再借出一个&str切片打印. - UTF-8 验证:对一个含中文或 emoji 的字符串,打印它的
.len()与.chars().count(),解释差异. - 预分配对比:分别用
Vec::new()和Vec::with_capacity(n)各 push 大量元素,思考后者好在哪.