Learn
Rust/25-strings-deep

字符串深入

Rust 的字符串是 UTF-8 编码,且分 String(拥有)与 &str(借用)两种形态。理解它们能避开大量新手坑。

1. String vs &str

let owned: String = String::from("你好");   // 堆上,可变、拥有
let borrowed: &str = "你好";                 // 只读、借用(字面量在二进制里)
  • String:可在堆上增长、修改,拥有数据。
  • &str:对 UTF-8 字节序列的视图,不可变、不拥有。
ℹ️函数参数为何用 &str

&str 既能接收 String 切片也能接收字面量,最通用。需要修改/构建字符串时才用 String。

2. UTF-8 编码陷阱

一个字符可能占多个字节。按字节索引会出错:

UTF-8 字节与字符
fn main() {
    let s = String::from("你好");
    // println!("{}", &s[0..1]);   // 运行错误:切在字符中间
    println!("字节数 = {}", s.len());              // 6,UTF-8 字节
    println!("字符数 = {}", s.chars().count());   // 2,按 Unicode 字符
}
⚠️不要按字节切字符串

中文字符在 UTF-8 中占 3 字节。对 String 用 [0..1] 切片极易落在字符中间,Rust 会直接 panic 来阻止产生非法 UTF-8。

3. 遍历字符

遍历字符
fn main() {
    for c in "你好abc".chars() {
        println!("{}", c);
    }
    // 若要字节:.bytes()
}

4. 拼接与转换

let mut s = String::from("Hello");
s.push_str(", Rust");
s.push('!');
let combined = format!("{} {}", s, 2024);
 
let n = 42;
let ns = n.to_string();          // 数字 -> String
let parsed: i32 = "42".parse().unwrap();   // &str -> 数字

5. &str 与 String 互转

let s: String = "hi".to_string();   // &str -> String
let r: &str = &s;                    // String -> &str(取切片)
let r2: &str = s.as_str();           // 等价写法
💡尽量晚地转成 String

很多函数接受 &str 即可,不必先 to_string()。只有需要所有权或修改时才升级为 String,能减少不必要的分配。

6. 常见坑速查

坑正确做法
&s[0..1] 切中文用 chars() / char_indices()
用 + 拼接 &str右侧须是 &str(s + &tail)
忘记 UTF-8 长度s.len() 是字节数,chars().count() 是字符数
🎯练习

写一个 fn reverse_chars(s: &str) -> String,按 Unicode 字符反转字符串(用 chars().rev().collect())。对「Rust语言」调用,验证是「言语tsuR」而非按字节乱码。

小结

  • String 拥有/可改;&str 借用/只读,最通用
  • UTF-8 下不要按字节切片,用 chars()
  • 拼接用 push_str/+/format!;解析用 parse()
  • 晚转 String、早用 &str 减少分配
  • 下一章看文件 IO →