rust字符串数据类型String、str、CString和CStr

在 Rust 中,处理字符串时有四种主要的类型:StringstrCString 和 CStr。理解它们之间的区别至关重要,特别是当需要与 C 语言代码进行交互时。

1. 核心区别概览 

类型是否拥有所有权是否可变用途适用场景
String Rust 原生,堆分配,动态大小 Rust 内部使用,可增长的字符串
str Rust 原生,不可变,编译时已知大小 字符串字面量、切片引用
CString C 兼容,堆分配,以 \0 结尾 需要传递拥有所有权的字符串给 C 函数时
CStr C 兼容,不可变,引用,以 \0 结尾 需要从 C 函数接收字符串引用或处理 C 风格字符串常量时

2. String 和 str (Rust 原生字符串)

这两个是用于日常 Rust 编程的标准字符串类型。
2.1 String
  • 特点: 拥有所有权,存储在堆上,可增长,UTF-8 编码。
  • 使用场景:
    • 构建在运行时需要修改或增长的字符串(例如用户输入、文件读取)。
    • 需要在函数间传递所有权时。
1)try_reserve

pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError>

  • 参数 additional:希望额外增加的容量大小不是总容量)。
  • 目标容量:系统会确保 capacity >= len + additional
 相关:
  • try_reserve_exact:要求分配器尽可能只分配你请求的精确大小。适用于你非常确定后续不会再有更多数据进入的场景,以节省内存。
2)truncate 
将字符串缩减到指定的长度(new_len)。
  • 如果 new_len 大于或等于当前长度:什么也不会发生。
  • 如果 new_len 小于当前长度:字符串会被截断,超出部分的字符会被丢弃。
  •  关键特性:不释放内存.这是开发者最需要注意的一点:truncate 只改变 len(长度),不改变 capacity(容量)。
与相关方法的对比
 
方法效果内存影响
truncate(n) 保留前 n 个字节 不释放内存,仅修改长度。
clear() 等同于 truncate(0) 不释放内存,保留缓冲区待复用。
pop() 移除最后一个字符 不释放内存。
shrink_to_fit() 尝试将容量缩减到当前长度 会重新分配内存,释放多余空间。
3)String::as_ptr
String::as_ptr 方法返回指向字符串底层内存缓冲区起始位置的原始指针*const u8)。
核心特性
  • 返回值:*const u8(指向第一个字节)。
  • 编码:由于 String 始终是 UTF-8 编码,该指针指向的也是 UTF-8 字节序列。
  • 所有权该方法不会转移所有权,String 对象仍然控制着这块内存
2.2 str (字符串切片,通常写作 &str)
  • 特点: 不拥有所有权,是对 String 或静态存储区的不可变引用,UTF-8 编码。
  • 使用场景:
    • 函数参数:高效地传递字符串数据而不转移所有权或复制数据。
    • 字符串字面量:let s = "hello"; 中 "hello" 的类型就是 &'static str
    • 从 String 中获取子串。
1)str::to_lowercase 
用于处理 Unicode 字符串大小写转换的标准方法。
其核心功能如下:
  • 处理对象:作用于 &strUTF-8 编码的字符串)。
  • 逻辑范围:遵循 完整的 Unicode 规范。它不仅处理 A-Z,还能处理带变音符号的字母(如 É → é)、希腊字母(如 Σ → σ)等。
  • 特殊转换:它能处理转换后长度发生变化的情况。例如,德语字母  在小写化后会变成两个字符 ss
  • 返回值:返回一个全新的 String。由于转换后字节长度可能改变,它必须在堆上分配新内存。

3. CString 和 CStr (C 语言兼容字符串)

这两个类型专门用于 Rust 和 C 语言代码(Foreign Function Interface, FFI)之间的互操作。C 语言要求字符串必须以空字符(null terminator, \0)结尾。
CString
  • 特点: 拥有所有权,存储在堆上,以 \0 结尾,内部不能包含 \0
  • 使用场景:
    • 最重要场景: 将一个 Rust 字符串传递给一个期望拥有所有权的 C 函数(例如调用 C 库的 API)。
    • 它提供安全保障,确保字符串符合 C 语言的要求,并且不包含意外的空字符。
use std::ffi::CString;

let c_string = CString::new("Hello C!").expect("CString::new failed");
// 现在可以将 c_string 传递给外部的 C 函数
unsafe {
    // some_c_function(c_string.as_ptr());
}
// 当 c_string 离开作用域时,内存安全释放
CStr
  • 特点: 不拥有所有权,是对内存中以 \0 结尾的 C 风格字符串的不可变引用,内部不能包含 \0
  • 使用场景
    • 最重要场景: 从 C 函数接收一个字符串指针(例如从 C 库读取配置字符串)。
    • 表示一个静态的 C 语言字符串常量。
use std::ffi::CStr;
use std::os::raw::c_char;

// 假设我们从 C 代码中获得一个指针
let c_ptr: *const c_char = /* get from C */;

// 使用 unsafe 块将其转换为安全的 CStr 引用
let c_str_ref = unsafe { CStr::from_ptr(c_ptr) };

// 可以将其转换为 Rust 原生的 &str 使用
let rust_str: &str = c_str_ref.to_str().expect("Contains invalid UTF-8");
println!("来自 C 的字符串:{}", rust_str);
总结要点
  1. 日常 Rust 编程: 只用 String 和 &str
  2. 与 C 语言 FFI 交互: 必须使用 CString(传递所有权到 C)和 CStr(从 C 获取引用)。
  3. 安全性: CString 和 CStr 保证了 C 语言要求的空字符结尾格式,并且它们的方法通常在 unsafe 块内使用,需要开发者确保指针的有效性。

 

posted @ 2025-11-28 10:22  PKICA  阅读(141)  评论(0)    收藏  举报