章节目录

String、&str、切片与集合

本章目标

这一章要把 Rust 初学者最常卡住的字符串讲清楚:String&str 的区别是什么,切片为什么不拥有数据,VecHashMap 如何存储集合,以及这些知识如何出现在 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,常见写法是 &ss.as_str()
  • HashMap 不保证遍历顺序。如果需要顺序,单独排序或使用其他结构。

练习

  1. 创建一个 Vec<String> 保存三个章节标题。
  2. 遍历并打印标题长度。
  3. 创建一个 HashMap<String, String> 保存两个 HTTP 响应头。
  4. 尝试对中文字符串按字节切片,观察错误或 panic。

造轮子任务

写一个简化版目录结构:用 Vec<String> 保存章节 slug,用 HashMap<String, String> 保存 slug 到标题的映射。实现一个函数 find_title(slug: &str) -> Option<&String>

小结

String 代表拥有,&str 代表借用视图。集合负责存放多个值,但不同集合有不同成本和语义。理解这些差异,是后面写数据结构和 Web 路由的基础。