Rust By Practice:深入理解String类型
String类型是Rust中最常用的字符串类型之一,它提供了UTF-8编码、可增长的动态字符串功能。本文将全面介绍String类型的使用方法、内部原理以及与&str的区别。
String基础操作
String类型拥有字符串内容的所有权,存储在堆上,可以动态增长。让我们看一个基础示例:
fn main() {
let mut s: String = String::from("hello, ");
s.push_str("world");
s.push('!');
move_ownership(s);
println!("Success!")
}
fn move_ownership(s: String) {
println!("ownership of \"{}\" is moved here!", s)
}
在这个例子中,我们:
- 使用
String::from
创建String - 使用
push_str
追加字符串 - 使用
push
追加单个字符 - 展示了String的所有权转移特性
String与&str的区别
String和&str的关系类似于Vec 和&[T]的关系:
- String:拥有所有权的字符串类型,存储在堆上,可增长
- &str:字符串切片引用,指向有效的UTF-8序列
fn main() {
let mut s = String::from("hello, world");
// 获取整个字符串的切片
let slice1: &str = &s[..];
// 获取部分字符串切片
let slice2 = &s[..5];
// 需要修改字符串时必须使用String
let mut s3 = s.clone();
s3.push('!');
}
内存分配与UTF-8编码
String基于UTF-8编码,这带来了一些特性:
- 无法直接通过索引访问字符,因为UTF-8是变长编码
- 可以通过切片获取部分字符串,但必须保证切片边界在字符边界上
fn main() {
let s = String::from("hello, 世界");
// 正确获取字符的方式
for (i, c) in s.chars().enumerate() {
if i == 7 {
assert_eq!(c, '世')
}
}
}
String内部结构
String实际上是一个智能指针,包含三个部分:
- 指向堆上字节数组的指针
- 已使用的长度
- 已分配的容量
use std::mem;
fn main() {
let story = String::from("Rust By Practice");
let mut story = mem::ManuallyDrop::new(story);
let ptr = story.as_mut_ptr();
let len = story.len();
let capacity = story.capacity();
// 安全重建String
let s = unsafe { String::from_raw_parts(ptr, len, capacity) };
assert_eq!("Rust By Practice", s);
}
性能优化
String在容量足够时不会重新分配内存:
fn main() {
let mut s = String::with_capacity(25);
println!("{}", s.capacity()); // 25
s.push_str("hello");
println!("{}", s.capacity()); // 25
s.push_str(" world");
println!("{}", s.capacity()); // 25
}
总结
String是Rust中处理字符串的主要类型,理解其所有权语义、UTF-8编码特性以及内部结构对于编写高效、安全的Rust代码至关重要。通过合理使用String和&str,可以在保证安全性的同时获得良好的性能表现。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考