String、&str、切片与集合
本章目标
这一章要把 Rust 初学者最常卡住的字符串讲清楚:String 和 &str 的区别是什么,切片为什么不拥有数据,Vec 和 HashMap 如何存储集合,以及这些知识如何出现在 Web 服务里。
它是什么
String 是可增长、拥有数据的 UTF-8 字符串。&str 是指向一段 UTF-8 字符串数据的借用视图。
let owned = String::from("hello");
let borrowed: &str = &owned;
owned 拥有内存。borrowed 只是临时看见这段文本,不负责释放。
集合是能存多个值的数据结构。常用集合包括:
Vec<T>:连续动态数组。HashMap<K, V>:键值映射。VecDeque<T>:双端队列。
为什么需要
Web 服务几乎处处是字符串:请求路径、请求头、HTML、Markdown、文件路径。你必须知道什么时候需要拥有字符串,什么时候只要借用。
如果函数只是读取文本,参数优先写 &str。如果函数要保存文本,通常需要 String。这能减少不必要复制,也能让 API 更灵活。
怎么使用
use std::collections::HashMap;
let mut headers = HashMap::new();
headers.insert("content-type".to_string(), "text/html".to_string());
let path = String::from("/docs/00-preface");
let is_docs = path.starts_with("/docs/");
HashMap 的 key 和 value 都是 String,因为请求解析后要把它们保存到 Request 结构体中。starts_with 只需要借用字符串内容,所以传入 &str 即可。
逐行解释
let mut chapters = Vec::new();
chapters.push(String::from("所有权"));
chapters.push(String::from("借用"));
for chapter in &chapters {
println!("{chapter}");
}
let mut chapters = Vec::new();创建一个可变动态数组。Vec::new()创建空集合。chapters.push(...)把元素追加到末尾。String::from("所有权")把字符串字面量复制成拥有所有权的String。for chapter in &chapters借用整个集合进行遍历。chapter的类型是&String,打印时会自动按字符串显示。- 使用
&chapters后,循环不会拿走集合所有权,循环结束仍可继续使用chapters。
常见坑
- 字符串索引不是按字节安全取字符,
s[0]在 Rust 中不允许。 - 中文是 UTF-8 多字节,按字节切片可能切在字符中间导致 panic。
String可以转成&str,常见写法是&s或s.as_str()。HashMap不保证遍历顺序。如果需要顺序,单独排序或使用其他结构。
练习
- 创建一个
Vec<String>保存三个章节标题。 - 遍历并打印标题长度。
- 创建一个
HashMap<String, String>保存两个 HTTP 响应头。 - 尝试对中文字符串按字节切片,观察错误或 panic。
造轮子任务
写一个简化版目录结构:用 Vec<String> 保存章节 slug,用 HashMap<String, String> 保存 slug 到标题的映射。实现一个函数 find_title(slug: &str) -> Option<&String>。
小结
String 代表拥有,&str 代表借用视图。集合负责存放多个值,但不同集合有不同成本和语义。理解这些差异,是后面写数据结构和 Web 路由的基础。