本页目录

字符串与 Path

&str 是 UTF-8 保证的字节切片引用(String 是其堆分配版本),不能按 s[0] 索引(因为 UTF-8 一个字符 1-4 字节,O(1) 索引不成立)。OsStr 是平台原生字符串(Unix 上就是字节,Windows 上是 WTF-8),PathOsStr 的路径封装——跨平台文件操作的正确做法是始终用 Path/PathBuf 而非字符串拼接。

String 与 &str: owned vs borrowed

let s: String = String::from("hello");  // 堆分配, 可变, owned
let r: &str = "hello";                  // 静态/借用, 不可变

String = Vec<u8> + UTF-8 validity guarantee——编译器保证 String 的内容始终是有效 UTF-8。可以通过 into_bytes() 取出底层 Vec<u8>(放弃 UTF-8 保证),也可以通过 from_utf8() 从 bytes 构造(返回 Result,无效 UTF-8 则 Err)。

为什么不能用 s[0]

在 C 中 s[0] 返回一个 byte——对 ASCII 没问题,对多语言完全不正确。在 Python 中 s[0] 返回第一个 Unicode code point——但 code point 不总是"可见字符"(é 可能是 e + ́,两个 code points)。

Rust 不把这种有隐藏成本的访问暴露为索引语法:

let s = "こんにちは";                  // 5 个字符, UTF-8 中是 15 bytes
// let c = s[0];                      // COMPILE ERROR: no indexing for String
let c = s.chars().nth(2).unwrap();    // 'に' — O(2): 必须跳过前 2 个字符

UTF-8 是变长编码:ASCII 字符 1 byte,大多数西欧/中东字符 2 bytes,中日韩字符 3 bytes,emoji 4 bytes。s[n] 在 Rust 中不能是 O(1)——所以干脆不提供。程序员必须显式写 .chars().nth(n),承认"这需要扫描"。

OsStr/OsString: 非 UTF-8 的系统字符串

Unix 上的文件名只是 [u8]——可以是任意 bytes,不保证 UTF-8。Windows 上的文件名是 WTF-8(接近 UTF-16)。OsStr 是跨平台的抽象——不对内容做 UTF-8 保证,但能安全传递和拼接:

use std::ffi::OsStr;
use std::path::{Path, PathBuf};

let p = Path::new("/usr/bin");
let parent = p.parent().unwrap();    // /usr
let file = p.file_name().unwrap();   // bin
let full: PathBuf = p.join("subdir").join("file.txt");  // /usr/bin/subdir/file.txt

Path 只是 OsStr 的包装,加了路径专用方法(parent, extension, components, join)。PathBuf 是 owned 版本。关键在于:Path 不要求内容转为 String——你可以直接在 Path 上操作,无需先做 UTF-8 检查。

参考

  • Rust Book: Chapter 8.2
  • Rust Reference: OsStr, Path

Keywords: String, &str, UTF-8, char, OsStr, Path, PathBuf, indexing, Unicode