Rust: String, &str, Vec<u8> 和 &[u8] — 转换与性能指南

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

“如何在 String、&str、Vec<u8> 和 &[u8] 之间转换?”—— 您的 Rust 优化检查清单

\n\n

如果您曾经用 Rust 编写过处理文本或字节的代码,您很可能遇到过这样的问题:“如何将 String 转换为 &str?那 Vec<u8> 转换为 &[u8] 呢?反过来又怎么做?” 这是 Stack Overflow 上最常见的问题之一(有数万次浏览),这并非偶然。看似简单的转换背后隐藏着陷阱,尤其是在涉及性能时。

\n\n

在本文中,我们不仅仅列举“如何做”的方法。我们将深入探讨为什么某些方法比其他方法更快,以及何时应该使用它们。您将学习如何避免不必要的复制、多余的内存分配,以及如何编写真正高效的 Rust 代码。

\n\n

为什么需要了解这些?背景与性能

\n

Rust 是一种让您控制内存的语言。但控制力也伴随着责任。字符串与字节之间的转换是最常见的操作之一。例如,在处理网络协议、数据序列化(JSON、Protobuf)或仅仅是读取文件时。选择错误的转换方式可能导致复制本可以无需复制就能使用的数据。在系统编程的世界里,这至关重要。

\n\n

让我们分析四种基本类型,并学习如何无损耗地在它们之间切换。

\n\n

String 和 &str:一枚硬币的两面

\n

String 是一个拥有所有权的、可变的、UTF-8 编码的字符串。&str 是一个借用的、不可变的字符串切片。主要规则是:String 是数据的所有者,&str 是对数据的引用。

\n\n

如何从 String 获取 &str(以及反向操作)

\n

这是最简单、最安全的情况。

\n\n
let my_string = String::from("Привет, мир!");\n// 1. 从 String 获取 &str(仅仅是借用)\nlet my_str: &str = &my_string;\n// 或者使用 as_str()\nlet my_str2: &str = my_string.as_str();\n\n// 2. 从 &str 获取 String(创建一个新的副本)\nlet new_string: String = my_str.to_string();\n// 或者\nlet new_string2: String = String::from(my_str);
\n\n

重要提示: 将 &str 转换为 String 总是会分配新的内存。这很昂贵。将 String 转换为 &str 仅仅是获取一个引用(零成本)。

\n\n

如何从 String 获取 Vec<u8>(以及反向操作)

\n

这里开始变得有趣,因为 String 内部实际上就是一个 Vec<u8>,并带有 UTF-8 有效性的保证。

\n\n
let my_string = String::from("Rust");\n\n// 3. String -> Vec<u8>\n// 选项 A(复制):安全但昂贵\nlet bytes_vec_copy: Vec<u8> = my_string.as_bytes().to_vec();\n\n// 选项 B(无复制,消费 String):\nlet bytes_vec_owned: Vec<u8> = my_string.into_bytes(); // my_string 不再存在!\n\n// 4. Vec<u8> -> String(检查 UTF-8 有效性)\nlet new_string: String = String::from_utf8(bytes_vec_owned).expect("无效的 UTF-8");\n// 如果您确定其有效性:\nlet new_string_unchecked: String = unsafe { String::from_utf8_unchecked(bytes_vec_copy) };
\n\n

关键点: into_bytes() 是一个零成本的转换。它获取内部的 Vec<u8> 缓冲区并将其交给您,不复制任何字节。as_bytes().to_vec() 则是显式的复制。

\n\n

from_utf8_unchecked 方法是 unsafe 的。仅当您 100% 确定字节是有效的 UTF-8 时才使用它。否则,程序可能会 panic 崩溃,或者更糟,导致未定义行为。

\n\n

Vec<u8> 和 &[u8]:字节切片

博客

书籍推荐