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);
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;
let a = String::from("hello");
let b = String::from("world");
let c = format!("{} {}", a, b);
let s = String::from("hello world");
let replaced = s.replace("world", "rust");
let parts: Vec<&str> = "a,b,c".split(',').collect();
let s = " hello ";
let trimmed = s.trim();
}
索引问题
fn main() {
let hello = String::from("hello");
let byte = hello.as_bytes()[0];
for c in hello.chars() {
println!("{}", c);
}
for b in hello.bytes() {
println!("{}", b);
}
let first = hello.chars().nth(0);
}
UTF-8 与中文
fn main() {
let s = String::from("你好世界");
println!("字节数: {}", s.len());
println!("字符数: {}", s.chars().count());
let first_two: String = s.chars().take(2).collect();
println!("{}", first_two);
}
类型转换
fn main() {
let s1: String = "hello".to_string();
let s2: String = String::from("hello");
let s3: String = "hello".to_owned();
let s = String::from("hello");
let slice: &str = &s;
let slice: &str = s.as_str();
let n = 42;
let s = n.to_string();
let s = format!("{}", n);
let n: i32 = "42".parse().unwrap();
let n: f64 = "3.14".parse().unwrap();
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");
let upper = s.to_uppercase();
let lower = s.to_lowercase();
}
实践练习
练习 1:字符串反转
fn reverse_string(s: &str) -> String {
s.chars().rev().collect()
}
fn main() {
println!("{}", reverse_string("hello"));
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"));
println!("{}", capitalize_first("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.chars().nth(0).unwrap();
2. 中文字符切片
let s = String::from("你好");
let first_char: String = s.chars().take(1).collect();
3. 性能陷阱
let mut result = String::new();
for i in 0..1000 {
result = result + &i.to_string();
}
let mut result = String::new();
for i in 0..1000 {
result.push_str(&i.to_string());
}
小结
| 操作 | 语法 |
|---|
| 创建 | String::from("..."), "..."..to_string() |
| 追加 | push, push_str |
| 拼接 | format!, + |
| 切片 | &s[a..b](按字节,需在字符边界) |
| 遍历 | chars(), bytes() |
| 转换 | parse(), to_string(), as_str() |