处理字符串和切片

RustBeginner
立即练习

简介

Rust 使用 String 表示拥有所有权且可增长的文本,使用 &str 表示文本的借用视图。字符串切片可以查看整个字符串,也可以查看指定的字节范围,而且不会复制其中的字符。

你将把拥有所有权的文本传递给一个借用 &str 的函数,创建一个借用的命令关键词,然后故意让编译器尝试以不受支持的方式索引 UTF-8 文本,最后使用符合有效边界的切片修复这个问题。

将拥有所有权的文本和字面量借用为 &str

在此步骤中,你会把两种不同的文本来源传递给同一个接受借用字符串切片的函数。

进入准备好的项目并打开源文件:

cd /home/labex/project/string-slices
nano src/main.rs

String 类型拥有可增长文本的所有权。&str 类型读作「字符串切片」,表示对有效 UTF-8 文本的借用视图。像 "preview" 这样的字符串字面量本身已经是借用的字符串切片类型,因为程序会在整个运行期间保存这段文本。

show_text(label: &str) 函数可以查看这两种文本。只将 /* Step 1: borrow command */ 替换为 &command:

    show_text(&command);

Rust 会自动将借用的 &String 视为函数所需的 &str 视图。这个转换不会复制或移动字符。

使用 Ctrl+O 保存,按 Enter 确认,然后使用 Ctrl+X 退出。运行程序:

cargo run --quiet

输出应为:

Text: serve reports
Text: preview
Keyword: <>
Status: ✅ ready

两次调用都会传入同一个 &str 参数。空的关键词是为下一步准备的占位符。

在不复制的情况下借用子字符串

在此步骤中,你会让 keyword 只查看 command 中的 serve 前缀。

打开源文件:

nano src/main.rs

切片形式 &text[start..end] 会借用一个范围。范围包含起始位置,但不包含结束位置。Rust 字符串范围按字节计算;serve 中的 ASCII 字母各占一个字节,因此范围从字节 0 开始,并在字节 5 之前结束。

只将步骤 2 所在行中的空字面量 "" 替换为 &command[0..5]:

    let keyword = &command[0..5]; // Step 2: replace the empty slice.

生成的 &str 会指向 command 内部的内容,不会分配或复制第二个字符串。由于 command 仍在作用域内,使用 keyword 时,这个借用视图仍然有效。

保存并退出,然后运行:

cargo run --quiet

现在,最后一行应为:

Keyword: <serve>

拥有所有权的 command 仍包含完整的 serve reports 文本,而 keyword 只是指向其前五个字节的借用窗口。

遵守 UTF-8 字符边界

在此步骤中,你会故意尝试使用整数索引,阅读编译器的说明,然后将其替换为有效的 UTF-8 切片。

UTF-8 字符并不都占用一个字节。在 "✅ ready" 中,勾选符号占用三个字节。因此,字节 0 不一定就是「第一个字符」,Rust 也不允许对 String 使用 status[0]。

打开文件,只删除步骤 3 打印行开头的 //:

nano src/main.rs

保存并退出,然后运行检查命令。此时应当失败:

cargo check

找到以下稳定的错误信息:

error[E0277]: the type `str` cannot be indexed by `{integer}`

这个诊断信息可以防止你把每个字符都当成位于单字节位置的字符。再次打开文件,将产生错误的打印行替换为下面两行:

    let symbol = &status[0..3];
    println!("Status symbol: {symbol}");

范围 0..3 恰好在这三个字节组成的符号之后结束。由于准备好的文本及其字节边界是已知的,因此这段代码可以正常工作;但它并不是在任意文本中查找第一个人类可见字符的通用方法。如果范围从 UTF-8 字符的中间截断,程序会在运行时发生 panic,因此字符串范围必须使用已知的有效边界。

保存并退出,然后检查并运行程序:

cargo check
cargo run --quiet

完整输出应为:

Text: serve reports
Text: preview
Keyword: <serve>
Status: ✅ ready
Status symbol: ✅

成功的输出表明,最后的切片选择了一个完整的 UTF-8 字符,而且没有复制原始的 String。

总结

你已经连接了拥有所有权的 String 值和借用的 &str 视图,在不分配新内存的情况下选择了子字符串,并了解了为什么不能把 UTF-8 文本当作按字节索引的字符数组。安全的字符串范围必须在有效的 UTF-8 边界处开始和结束。