Ch 15: String字符串
String是Rust中可变的、堆分配的字符串类型。与&str(字符串切片)不同,String拥有其数据的所有权。
String的内部结构:
struct String { vec: Vec<u8>, // UTF-8编码的字节向量}今天我们学习String的创建、修改和内存管理。
2. 创建String
Section titled “2. 创建String”2.1 常用创建方法
Section titled “2.1 常用创建方法”fn main() { // 空字符串 let s1 = String::new();
// 从字面量创建(&str自动转换) let s2 = String::from("hello"); let s3 = "world".to_string();
// 带容量的空字符串(预分配内存) let s4 = String::with_capacity(100);
println!("s1: '{:?}', len: {}", s1, s1.len()); println!("s2: '{}', len: {}, capacity: {}", s2, s2.len(), s2.capacity()); println!("s4 capacity: {}", s4.capacity());}2.2 从字节创建
Section titled “2.2 从字节创建”fn main() { // 从字节数组创建(必须是有效的UTF-8) let bytes = [104, 101, 108, 108, 111]; // "hello" let s = String::from_utf8(bytes.to_vec()).unwrap(); println!("{}", s);
// 使用from_utf8_lossy处理无效UTF-8 let invalid_bytes = [0x80, 0x81, 0x82]; let s_lossy = String::from_utf8_lossy(&invalid_bytes); println!("{}", s_lossy); // 输出: ��� (用U+FFFD替换无效序列)}3. 追加内容
Section titled “3. 追加内容”3.1 push与push_str
Section titled “3.1 push与push_str”fn main() { let mut s = String::from("hello");
// push 追加单个字符 s.push(','); println!("push后: {}", s); // 输出: hello,
// push_str 追加字符串切片 s.push_str(" world!"); println!("push_str后: {}", s); // 输出: hello, world!}3.2 使用format!拼接
Section titled “3.2 使用format!拼接”fn main() { let name = "Alice"; let age = 30;
// format! 格式化拼接 let s = format!("{} is {} years old", name, age); println!("{}", s);
// 重复拼接 let separator = "-".to_string(); let line = separator.repeat(10); println!("{}", line); // 输出: ----------}4. capacity内存管理
Section titled “4. capacity内存管理”String内部使用Vec存储,所以有capacity的概念。
4.1 理解capacity
Section titled “4.1 理解capacity”fn main() { let mut s = String::with_capacity(10); println!("初始capacity: {}", s.capacity());
s.push_str("hello"); println!("追加后capacity: {}", s.capacity());
// capacity不会自动缩减 s.shrink_to_fit(); println!("shrink后capacity: {}", s.capacity());
// shrink_to指定最小capacity s.shrink_to(5); println!("shrink_to(5)后capacity: {}", s.capacity());}注意:shrink_to_fit和shrink_to会尝试释放多余的内存,但具体行为取决于实现。
4.2 容量与重新分配
Section titled “4.2 容量与重新分配”fn main() { let mut s = String::with_capacity(5); println!("初始: len={}, cap={}", s.len(), s.capacity());
s.push_str("abc"); println!("追加abc后: len={}, cap={}", s.len(), s.capacity());
// 再追加会导致重新分配 s.push_str("de"); println!("追加de后: len={}, cap={}", s.len(), s.capacity());}重新分配是昂贵的操作,如果知道最终大小,预分配可以避免多次重新分配。
5. from_utf8详解
Section titled “5. from_utf8详解”from_utf8将字节转换为String,会检查UTF-8有效性:
fn main() { // 有效的UTF-8 let valid_bytes = b"hello world"; match String::from_utf8(valid_bytes.to_vec()) { Ok(s) => println!("有效: {}", s), Err(e) => println!("错误: {:?}", e), }
// 无效的UTF-8 let invalid_bytes = b"hello \xFF\xFE world"; match String::from_utf8(invalid_bytes.to_vec()) { Ok(s) => println!("有效: {}", s), Err(e) => { println!("UTF-8错误: {}", e); println!("无效字节: {:?}", e.into_bytes()); } }}6. 字符串修改操作
Section titled “6. 字符串修改操作”6.1 替换与移除
Section titled “6.1 替换与移除”fn main() { let mut s = String::from("hello world");
// replace 替换所有匹配 let s2 = s.replace("world", "rust"); println!("replace: {}", s2); // 输出: hello rust
// retain 只保留满足条件的字符 let mut s3 = String::from("hello123world456"); s3.retain(|c| c.is_alphabetic()); println!("retain: {}", s3); // 输出: helloworld}6.2 转换为其他类型
Section titled “6.2 转换为其他类型”fn main() { let s = String::from("42");
// parse 转换为数值类型 let num: i32 = s.parse().unwrap(); println!("parse: {}", num + 8);
// into_bytes 转换为字节数组 let bytes = s.into_bytes(); println!("bytes: {:?}", bytes);
// into_boxed_str 转换为Box<str> let boxed: Box<str> = String::from("hello").into_boxed_str(); println!("boxed len: {}", boxed.len());}7. 常用方法汇总
Section titled “7. 常用方法汇总”| 方法 | 说明 |
|---|---|
new() / from() | 创建空字符串 |
with_capacity(n) | 创建带容量的字符串 |
push(c) | 追加字符 |
push_str(s) | 追加字符串 |
pop() | 移除并返回最后一个字符 |
truncate(n) | 截断到n个字符 |
clear() | 清空字符串 |
len() | 字节长度 |
is_empty() | 是否为空 |
capacity() | 当前容量 |
reserve(n) | 预留容量 |
shrink_to_fit() | 收缩到合适大小 |
replace(old, new) | 替换 |
parse::<T>() | 解析为其他类型 |
into_bytes() | 转为字节数组 |
8. 注意事项
Section titled “8. 注意事项”- UTF-8编码:String是UTF-8编码的,一个字符可能占用1-4个字节。
len()返回字节数,而非字符数。 - 按字节索引:不能用整数索引字符串,应使用
chars()迭代器。 - 容量预分配:如果知道最终大小,预先分配容量可以避免重新分配。
fn main() { let s = "hello";
// 错误:按字节索引 // println!("{}", s[0]); // 编译错误!
// 正确:迭代字符 for c in s.chars() { print!("{} ", c); } println!(); // 输出: h e l l o}今天我们学习了String的核心操作:
- 创建方法 -
new()、from()、to_string()、with_capacity() - 追加操作 -
push、push_str、format! - 容量管理 -
capacity、reserve、shrink_to - UTF-8处理 -
from_utf8、from_utf8_lossy - 修改操作 -
replace、retain、truncate
String是Rust中处理文本的基础类型,正确理解其UTF-8编码和容量管理对于写出高效的Rust代码至关重要。