Rust 字符串

String 与 &str 的分工:拥有型 UTF-8 缓冲 vs 借用切片;索引、拼接与常见陷阱。

#type / concept #status / growing #tech / dev #resource / rust

[!info] 关联笔记

Rust 字符串

这个概念为什么出现

文本处理无处不在,但 Rust 故意把“字符串”拆成多层:

  • 字面量与借用视图:&str
  • 可增长拥有缓冲:String
  • 底层字节:[u8] / Vec<u8>

还要面对 UTF-8:不能像某些语言一样按“字符下标”廉价随机改。

[!abstract] 一句话理解 &str 是 UTF-8 字符串切片(借用);String 是堆上可增长的拥有型 UTF-8 缓冲;索引按字节,操作必须落在字符边界。

最小可运行示例

场景:用户中心规范化显示名

注册流把用户输入 " Ada Lovelace " 规范化为小写 trim 后的拥有字符串,并统计字节长度用于存储估算。

// 业务意图:trim + lowercase 得到可存储的 display_name。
// 教学点:&str 输入、String 输出;to_lowercase 产生新 String;len 是字节数。

fn normalize_display_name(raw: &str) -> String {
    let trimmed = raw.trim(); // &str 切片,仍借用 raw
    trimmed.to_lowercase()    // 得到拥有的 String
}

fn main() {
    let input = "  Ada Lovelace  ";
    let name = normalize_display_name(input);
    println!("display_name={name}");
    println!("byte_len={}", name.len()); // UTF-8 字节长度
    println!("chars={}", name.chars().count());
}

建议运行:

cargo run

期望输出:

display_name=ada lovelace
byte_len=12
chars=12

结合场景再看三个关注点

  1. API 收 &str、返回 String:调用方用字面量或已有 String 都行。
  2. len() 是字节:含中文时字节数 ≠ 字符数。
  3. 不要 s[0] 取字符String/str 不支持按 usize 直接索引字符。

核心概念与准确模型

类型拥有?可变增长?典型来源
&str否(视图)字面量、String 借用
StringString::fromto_stringformat!
&[u8]原始字节

常用构造:

let s = String::from("hi");
let t = "hi".to_string();
let u = format!("user={id}", id=7);

拼接:

let mut s = String::from("hello");
s.push_str(", world");
s.push('!');
let both = s + "!!"; // + 会吃掉左侧 String 所有权

遍历:chars()bytes()、或按 split_whitespace 等。

设计动机

  • UTF-8 默认正确处理全球文本
  • 区分借用/拥有,避免隐式拷贝
  • 强制处理编码边界,减少半吊子截断

边界情况与反直觉行为

  • 中文等字符多字节:&s[0..1] 可能 panic
  • + 运算符对 String 的所有权特殊
  • OsString/Path/CString 是另一类字符串边界(OS/FFI)

常见误区

[!warning] 常见误区:用下标取“第 n 个字符” 正确做法:chars().nth(n)(O(n))或用 crate 处理字形簇。

[!warning] 常见误区:把 Stringstr 当成无关类型 str 是 DST;StringDerefstr,故很多方法在两者上都能调。

工程实践

  1. 函数参数优先 &str
  2. 需要拥有再 String
  3. 日志/格式化用 format!write!
  4. 性能敏感路径避免无谓分配

本节总结

  • &str 借用,String 拥有
  • UTF-8 字节索引要落在边界
  • 文本 API 设计先想清所有权

自测题

  1. 为什么 s[0]String 不成立?
  2. fn f(s: String)fn f(s: &str) 的调用体验差在哪?
参考答案
  1. 字符串按 UTF-8 编码,单字节下标不一定是字符边界,且类型未实现 Index<usize> 取 char。
  2. String 会强制调用方交出或 clone;&str 更通用。

延伸阅读与资料来源

资料类型支撑内容
The Book — Storing UTF-8 Encoded Text with Strings官方书String
std::string::String标准库API
std::str标准库&str 方法
创建于 2026/7/15 更新于 2026/7/15