String 与 &str
学习目标
- 理解
String和&str的区别 - 掌握字符串的创建和操作
- 理解 UTF-8 编码的影响
- 掌握字符串与其他类型的转换
核心概念
String vs &str
String(堆分配,可变,拥有所有权)
┌──────────┐ 堆内存
│ ptr ────────→ [h, e, l, l, o]
│ len: 5 │
│ cap: 8 │ ← 预分配的容量
└──────────┘
&str(切片引用,不可变,借用)
┌──────────┐
│ ptr ────────→ 某处的 UTF-8 字节
│ len: 5 │
└──────────┘
| 特性 | String | &str |
|---|---|---|
| 存储 | 堆 | 引用 |
| 可变 | 是 | 否 |
| 所有权 | 拥有 | 借用 |
| 大小 | 24 字节(ptr+len+cap) | 16 字节(ptr+len) |
创建字符串
fn main() {
// 从字面量创建
let s1 = String::from("hello");
let s2 = "hello".to_string();
let s3 = "hello".to_owned();
// 空字符串
let s4 = String::new();
// 带容量
let s5 = String::with_capacity(100);
// 字符串字面量(&str 类型)
let s6: &str = "hello";
}
字符串操作
fn main() {
let mut s = String::from("hello");
// 追加
s.push('!'); // 追加单个字符
s.push_str(" world"); // 追加字符串切片
// 拼接
let s1 = String::from("hello");
let s2 = String::from(" world");
let s3 = s1 + &s2; // 注意:s1 被移动,s2 被借用
// println!("{}", s1); // ❌ s1 已失效
// format!(推荐,不移动任何值)
let a = String::from("hello");
let b = String::from("world");
let c = format!("{} {}", a, b); // a, b 仍有效
// 替换
let s = String::from("hello world");
let replaced = s.replace("world", "rust"); // "hello rust"
// 分割
let parts: Vec<&str> = "a,b,c".split(',').collect();
// ["a", "b", "c"]
// 修剪
let s = " hello ";
let trimmed = s.trim(); // "hello"
}
索引问题
fn main() {
let hello = String::from("hello");
// ✅ 按字节索引
let byte = hello.as_bytes()[0]; // 104 ('h' 的 ASCII)
// ❌ 不能直接用字符串索引
// let c = hello[0]; // 编译错误
// ✅ 用 chars() 遍历
for c in hello.chars() {
println!("{}", c);
}
// ✅ 用 bytes() 遍历字节
for b in hello.bytes() {
println!("{}", b);
}
// ✅ 按字符索引
let first = hello.chars().nth(0); // Some('h')
}
UTF-8 与中文
fn main() {
let s = String::from("你好世界");
println!("字节数: {}", s.len()); // 12(每个中文字符 3 字节)
println!("字符数: {}", s.chars().count()); // 4
// ❌ 在非字符边界切片会 panic
// let slice = &s[0..2];
// ✅ 安全的切片方式
let first_two: String = s.chars().take(2).collect();
println!("{}", first_two); // "你好"
}
类型转换
fn main() {
// &str → String
let s1: String = "hello".to_string();
let s2: String = String::from("hello");
let s3: String = "hello".to_owned();
// String → &str
let s = String::from("hello");
let slice: &str = &s;
let slice: &str = s.as_str();
// 数字 → String
let n = 42;
let s = n.to_string();
let s = format!("{}", n);
// String → 数字
let n: i32 = "42".parse().unwrap();
let n: f64 = "3.14".parse().unwrap();
// Vec<u8> → String
let bytes = vec![104, 101, 108, 108, 111];
let s = String::from_utf8(bytes).unwrap();
}
常用方法
fn main() {
let s = String::from("Hello, World!");
// 查询
println!("len: {}", s.len()); // 字节数
println!("is_empty: {}", s.is_empty()); // 是否为空
println!("contains: {}", s.contains("World")); // 是否包含
println!("starts_with: {}", s.starts_with("Hello")); // 是否以开头
println!("ends_with: {}", s.ends_with("!")); // 是否以结尾
// 查找
let pos = s.find("World"); // Some(7)
// 大小写
let upper = s.to_uppercase(); // "HELLO, WORLD!"
let lower = s.to_lowercase(); // "hello, world!"
}
实践练习
练习 1:字符串反转
fn reverse_string(s: &str) -> String {
s.chars().rev().collect()
}
fn main() {
println!("{}", reverse_string("hello")); // "olleh"
println!("{}", reverse_string("你好世界")); // "界世好你"
}
练习 2:统计字符
fn char_count(s: &str) -> std::collections::HashMap<char, usize> {
let mut map = std::collections::HashMap::new();
for c in s.chars() {
*map.entry(c).or_insert(0) += 1;
}
map
}
fn main() {
let counts = char_count("hello world");
for (ch, count) in &counts {
println!("'{}': {}", ch, count);
}
}
练习 3:首字母大写
fn capitalize_first(s: &str) -> String {
let mut chars = s.chars();
match chars.next() {
None => String::new(),
Some(first) => {
let upper: String = first.to_uppercase().collect();
upper + chars.as_str()
}
}
}
fn main() {
println!("{}", capitalize_first("hello")); // "Hello"
println!("{}", capitalize_first("rust")); // "Rust"
println!("{}", capitalize_first("")); // ""
}
练习 4:单词计数
fn word_count(text: &str) -> usize {
text.split_whitespace().count()
}
fn main() {
let text = "Rust is a systems programming language";
println!("单词数: {}", word_count(text));
}
常见错误
1. 字符串索引
let s = String::from("hello");
// let c = s[0]; // ❌ 不能索引 String
let c = s.chars().nth(0).unwrap(); // ✅
2. 中文字符切片
let s = String::from("你好");
// let slice = &s[0..1]; // ❌ panic: not a char boundary
let first_char: String = s.chars().take(1).collect(); // ✅
3. 性能陷阱
// ❌ 在循环中用 + 拼接(每次创建新 String)
let mut result = String::new();
for i in 0..1000 {
result = result + &i.to_string(); // O(n²)
}
// ✅ 用 push_str 或 format!
let mut result = String::new();
for i in 0..1000 {
result.push_str(&i.to_string()); // O(n)
}
小结
| 操作 | 语法 |
|---|---|
| 创建 | String::from("..."), "..."..to_string() |
| 追加 | push, push_str |
| 拼接 | format!, + |
| 切片 | &s[a..b](按字节,需在字符边界) |
| 遍历 | chars(), bytes() |
| 转换 | parse(), to_string(), as_str() |
练习编辑器
rust
Loading...