Rust 提供丰富的字符串类型以适应不同场景,主要分为UTF-8文本、字节数据和系统路径三类。
1. UTF-8 文本类型
&str - 字符串切片
rust
// 不可变UTF-8文本引用
let s: &str = "Hello World"; // 字符串字面量(静态生命周期)
let from_string: &str = &String::from("动态"); // 从String借用
// 特性:零成本、不可变、UTF-8保证
fn process(text: &str) {} // 函数参数首选&str
process("字面量"); // 自动转换
String - 堆分配字符串
rust
// 可变、拥有所有权的UTF-8字符串
let mut s = String::from("Hello");
s.push_str(" Rust!");
s += "!"; // 追加操作
// 内存布局:指针+容量+长度(堆分配)
println!("len={}, cap={}", s.len(), s.capacity());
&'static str - 静态字符串
rust
// 编译时已知,存储在二进制文件的只读段
const GREETING: &'static str = "Hello";
static WARNING: &'static str = "Don't panic!";
// 应用:常量、配置、错误信息
fn default_name() -> &'static str {
"Anonymous"
}
Cow<'a, str> - 写时复制
rust
use std::borrow::Cow;
// 智能指针:可能是借用或拥有
fn to_uppercase(s: &str) -> Cow<str> {
if s.chars().any(|c| c.is_lowercase()) {
Cow::Owned(s.to_uppercase())
} else {
Cow::Borrowed(s)
}
}
2. 字节数据类型
&[u8] - 字节切片
rust
// 任意字节数据的引用(不保证UTF-8)
let bytes: &[u8] = b"Hello"; // 字节字面量
let from_str: &[u8] = "text".as_bytes();
// 处理二进制数据
for byte in bytes {
println!("{}", byte); // 72, 101, 108, 108, 111
}
// 转换回字符串(需有效UTF-8)
let text = std::str::from_utf8(bytes).unwrap();
Vec<u8> - 字节向量
rust
// 可变的字节集合(堆分配)
let mut bytes = Vec::new();
bytes.extend_from_slice(b"Hello");
bytes.push(b'!'); // 追加字节
// 常见用途:网络协议、文件I/O、加密
fn read_file() -> Vec<u8> {
std::fs::read("data.bin").unwrap()
}
// 转换为String(需有效UTF-8)
let text = String::from_utf8(bytes).unwrap();
let lossy = String::from_utf8_lossy(&[0xFF, 0xFE]); // 替换无效字符
&[u8; N] - 固定大小字节数组引用
rust
// 编译时已知大小的字节数组引用
let array: &[u8; 16] = b"0123456789ABCDEF";
let slice: &[u8] = &array[..]; // 可转换为切片
// MD5/SHA哈希值常用
let hash: &[u8; 32] = &[0u8; 32]; // 256位哈希
// 网络协议头
struct PacketHeader {
magic: &'static [u8; 4], // 固定标识
data: Vec<u8>,
}
&u8 - 单个字节引用
rust
// 极少单独使用,通常作为切片元素
let bytes = b"ABC";
let first_byte: &u8 = &bytes[0]; // &65
// 通常用于模式匹配
match bytes.get(0) {
Some(&b'A') => println!("Starts with A"),
Some(byte_ref) => println!("Byte: {}", byte_ref),
None => println!("Empty"),
}
3. 操作系统字符串类型
OsString / OsStr - 平台原生字符串
rust
use std::ffi::{OsString, OsStr};
// 平台原生编码(Windows: UTF-16, Unix: 任意8位字节)
let os_string = OsString::from("C:\\Program Files"); // 拥有所有权
let os_str: &OsStr = os_string.as_ref(); // 借用
// 从Path转换
let path = std::path::Path::new("/usr/bin");
let os_str_from_path: &OsStr = path.as_os_str();
// 转换为String(可能失败)
if let Ok(string) = os_string.into_string() {
println!("Valid UTF-8: {}", string);
}
CString / CStr - C兼容字符串
rust
use std::ffi::{CString, CStr};
// 以空字符结尾的C风格字符串
let c_string = CString::new("Hello").unwrap(); // 确保无内部\0
let c_str: &CStr = c_string.as_ref(); // &CStr借用
// FFI调用
extern "C" {
fn puts(s: *const std::os::raw::c_char);
}
unsafe {
puts(c_string.as_ptr()); // 传递到C函数
}
// 从C接收字符串
unsafe {
let c_str = CStr::from_ptr(raw_c_pointer);
let rust_str = c_str.to_str().unwrap();
}
4. 路径类型
Path / PathBuf - 路径操作
rust
use std::path::{Path, PathBuf};
// Path - 不可变路径引用(类似&str)
let path: &Path = Path::new("/home/user/file.txt");
let parent = path.parent(); // Some(Path("/home/user"))
// PathBuf - 可变、拥有所有权的路径(类似String)
let mut path_buf = PathBuf::from("/home");
path_buf.push("user");
path_buf.set_file_name("data.txt");
// 路径与字符串转换
let str_path: &str = path.to_str().unwrap(); // 可能失败
let os_str: &std::ffi::OsStr = path.as_os_str();
// 跨平台路径构建
let path = Path::new("dir").join("subdir").with_extension("txt");
5. 类型转换关系
UTF-8类型转换图
&'static str → &str ←→ String
↓ ↓ ↓
&[u8; N] → &[u8] ←→ Vec<u8>
转换方法总结
rust
// &str ↔ String
let s: String = "hello".to_string();
let slice: &str = &s;
// &str ↔ &[u8]
let bytes: &[u8] = "text".as_bytes();
let str_from_bytes = std::str::from_utf8(bytes).unwrap();
// String ↔ Vec<u8>
let bytes: Vec<u8> = "text".into_bytes();
let string = String::from_utf8(bytes).unwrap();
// OsString ↔ String
let os_string = OsString::from("path");
let string = os_string.into_string().unwrap(); // Result
let os_from_string = OsString::from(string);
// Path ↔ String
let path = Path::new("/tmp");
let string = path.to_str().unwrap(); // Option<&str>
let path_from_string = Path::new(string);
6. 性能与内存特征
| 类型 | 所有权 | 可变性 | 内存位置 | UTF-8保证 | 典型用途 |
|---|---|---|---|---|---|
&str |
借用 | ❌ 不可变 | 栈/静态 | ✅ 保证 | 函数参数、文本处理 |
String |
拥有 | ✅ 可变 | 堆 | ✅ 保证 | 动态构建文本 |
&[u8] |
借用 | ❌ 不可变 | 栈/堆 | ❌ 不保证 | 二进制数据处理 |
Vec<u8> |
拥有 | ✅ 可变 | 堆 | ❌ 不保证 | 文件I/O、网络数据 |
&[u8; N] |
借用 | ❌ 不可变 | 栈 | ❌ 不保证 | 固定大小数据(哈希、密钥) |
OsStr |
借用 | ❌ 不可变 | 平台相关 | ❌ 不保证 | 系统API参数 |
OsString |
拥有 | ✅ 可变 | 堆 | ❌ 不保证 | 跨平台路径处理 |
CStr |
借用 | ❌ 不可变 | C堆 | ❌ 不保证 | FFI字符串参数 |
CString |
拥有 | ✅ 可变 | 堆 | ✅ 保证(无内嵌\0) | FFI字符串构建 |
Path |
借用 | ❌ 不可变 | 栈 | ❌ 不保证 | 路径操作 |
PathBuf |
拥有 | ✅ 可变 | 堆 | ❌ 不保证 | 路径构建 |
7. 最佳实践指南
选择原则
- 文本处理 :优先使用
&str参数,返回String或Cow<str> - 二进制数据 :使用
&[u8]或Vec<u8>,明确区分文本和字节 - 系统交互 :使用
OsString/OsStr处理路径和系统调用 - FFI调用 :使用
CString/CStr确保空字符终止 - 固定数据 :常量使用
&'static str,固定大小数据用&[u8; N]
常见模式
rust
// 1. 通用文本处理函数
fn process_text(text: &str) -> String {
// 接受&str,返回String
text.to_uppercase()
}
// 2. 二进制数据管道
fn process_bytes(data: &[u8]) -> Vec<u8> {
data.iter().map(|b| b ^ 0xFF).collect()
}
// 3. 系统路径构建
fn build_path(base: &Path, filename: &str) -> PathBuf {
let mut path = base.to_path_buf();
path.push(filename);
path
}
// 4. FFI安全包装
fn call_c_function(text: &str) -> Result<String, std::ffi::NulError> {
let c_string = CString::new(text)?;
unsafe {
// 调用C函数
let result = foreign_function(c_string.as_ptr());
Ok(result.to_string_lossy().into_owned())
}
}
// 5. 类型安全API设计
struct Config {
name: String, // 需要所有权
description: Option<String>, // 可能为空
tags: Vec<String>, // 动态集合
version: &'static str, // 编译时常量
binary_data: Vec<u8>, // 非文本数据
install_path: PathBuf, // 系统路径
}
性能优化
rust
// 避免不必要的分配
fn efficient_join(strings: &[&str]) -> String {
let total_len: usize = strings.iter().map(|s| s.len()).sum();
let mut result = String::with_capacity(total_len);
for s in strings {
result.push_str(s);
}
result
}
// 使用slice代替Vec小数据
fn hash_prefix(data: &[u8]) -> [u8; 16] {
let mut hash = [0u8; 16];
// ... 计算哈希
hash
}
// Cow优化:延迟分配
fn maybe_uppercase(input: &str) -> Cow<str> {
if input.contains('a'..='z') {
Cow::Owned(input.to_uppercase())
} else {
Cow::Borrowed(input)
}
}
8. 错误处理模式
rust
// UTF-8转换错误处理
fn safe_from_utf8(bytes: Vec<u8>) -> Result<String, String> {
String::from_utf8(bytes)
.map_err(|e| format!("Invalid UTF-8: {}", e))
}
// 路径转换可能失败
fn get_path_string(path: &Path) -> Option<&str> {
path.to_str() // 返回Option,因为可能包含无效UTF-8
}
// 系统字符串转换
fn os_to_string(os_str: &OsStr) -> Result<String, &'static str> {
os_str.to_str()
.map(|s| s.to_string())
.ok_or("包含非UTF-8字符")
}
总结
Rust的字符串类型系统提供了:
- 类型安全:编译时区分文本、二进制、路径
- 零成本抽象:UTF-8验证只在必要时进行
- 跨平台兼容:通过OsString处理系统差异
- 内存安全:所有权系统防止悬垂指针
黄金法则:
- 文本用
str/String,二进制用[u8]/Vec<u8> - 系统交互用
OsStr/OsString - FFI用
CStr/CString - 路径用
Path/PathBuf - 函数参数优先最通用类型(
&str,&[u8],&Path)