Rust 集合与字符串
`Vec`、`HashMap`、`HashSet`、`String` 的常用操作与坑。
Key Points`HashMap` 的 `entry` API 是计数、缓存的标准写法,一次查找搞定。
Vec
Rust
fn main() {
let mut v: Vec<i32> = Vec::new();
v.push(1);
v.push(2);
v.push(3);
println!("{:?}", v);
println!("{}", v.len());
// 安全取值
match v.get(10) {
Some(x) => println!("{}", x),
None => println!("越界"),
}
// 删除
v.pop();
v.remove(0);
println!("{:?}", v);
// 保留满足条件的
let mut nums = vec![1, 2, 3, 4, 5, 6];
nums.retain(|x| x % 2 == 0);
println!("{:?}", nums);
}Output
[1, 2, 3]
3
越界
[2]
[2, 4, 6]v[i] panics out of range while v.get(i) returns Option. Indexing is fast, get is safe — use get for indices from external input.
HashMap
Rust
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert("蓝队", 10);
scores.insert("黄队", 50);
println!("{:?}", scores.get("蓝队"));
// 不存在才插入
scores.entry("红队").or_insert(0);
// 计数:entry API 的经典用法
let text = "the quick brown fox jumps over the lazy dog the end";
let mut counts = HashMap::new();
for word in text.split_whitespace() {
*counts.entry(word).or_insert(0) += 1;
}
let mut pairs: Vec<_> = counts.iter().collect();
pairs.sort_by(|a, b| b.1.cmp(a.1).then(a.0.cmp(b.0)));
for (w, c) in pairs.iter().take(3) {
println!("{} {}", w, c);
}
}Output
Some(10)
the 3
brown 1
dog 1| Method | Behaviour |
|---|---|
insert | 插入,返回旧值 |
get / get_mut | 取引用 |
entry(k).or_insert(v) | 不存在则插入,返回 &mut V |
entry(k).or_default() | 不存在则用 Default |
remove | 删除并返回 |
contains_key | 是否存在 |
entry hashes once. Writing if !m.contains_key(k) { m.insert(k, 0); } *m.get_mut(k).unwrap() += 1 hashes three times — slower and noisier.
HashSet
Rust
use std::collections::HashSet;
fn main() {
let a: HashSet<i32> = [1, 2, 3, 4].into_iter().collect();
let b: HashSet<i32> = [3, 4, 5, 6].into_iter().collect();
let mut inter: Vec<_> = a.intersection(&b).copied().collect();
inter.sort();
println!("交集 {:?}", inter);
let mut uni: Vec<_> = a.union(&b).copied().collect();
uni.sort();
println!("并集 {:?}", uni);
let mut diff: Vec<_> = a.difference(&b).copied().collect();
diff.sort();
println!("差集 {:?}", diff);
// 去重
let v = vec![1, 1, 2, 3, 3];
let uniq: HashSet<_> = v.iter().collect();
println!("{}", uniq.len());
}Output
交集 [3, 4]
并集 [1, 2, 3, 4, 5, 6]
差集 [1, 2]
3String
Rust
fn main() {
let mut s = String::from("Hello");
s.push_str(", world");
s.push('!');
println!("{}", s);
// 拼接
let a = String::from("foo");
let b = String::from("bar");
let c = format!("{}{}", a, b);
println!("{}", c);
println!("{} {}", a, b); // format! 不移动
let d = a + &b; // + 会移动 a
println!("{}", d);
// 常用
println!("{}", s.to_uppercase());
println!("{}", s.replace("world", "Rust"));
println!("{}", s.contains("Hello"));
println!("{}", s.len()); // 字节数
println!("{}", s.chars().count()); // 字符数
}Output
Hello, world!
foobar
foo bar
foobar
HELLO, WORLD!
Hello, Rust!
true
13
13a + &b **moves a** because Add is implemented as fn add(self, other: &str). Use format! or a.clone() + &b to keep it — the classic "why is a gone" moment.字符串与字符
Rust
fn main() {
let s = "Hello, 世界";
// 按字符
for c in s.chars() {
print!("{} ", c);
}
println!();
// 按字节
println!("字节数 {}", s.len());
println!("字符数 {}", s.chars().count());
// 反转(按字符,不是字节)
let rev: String = s.chars().rev().collect();
println!("{}", rev);
// 按分隔符拆
for part in "a,b,c".split(',') {
print!("{} ", part);
}
println!();
}Output
H e l l o , 世 界
字节数 13
字符数 9
界世 ,olleH
a b c s.len() is 13 (7 ASCII plus comma and space plus 3 bytes per CJK char) while chars().count() is 9. That mismatch is the single most confusing thing about Rust strings.
Vec
Rust
fn main() {
let mut v: Vec<i32> = Vec::new();
v.push(1);
v.push(2);
v.push(3);
println!("{:?}", v);
println!("{}", v.len());
// 安全取值
match v.get(10) {
Some(x) => println!("{}", x),
None => println!("越界"),
}
// 删除
v.pop();
v.remove(0);
println!("{:?}", v);
// 保留满足条件的
let mut nums = vec![1, 2, 3, 4, 5, 6];
nums.retain(|x| x % 2 == 0);
println!("{:?}", nums);
}Output
[1, 2, 3]
3
越界
[2]
[2, 4, 6]v[i] 越界会 panic,v.get(i) 返回 Option。索引访问快,get 安全——处理外部输入的下标时用 get。
HashMap
Rust
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert("蓝队", 10);
scores.insert("黄队", 50);
println!("{:?}", scores.get("蓝队"));
// 不存在才插入
scores.entry("红队").or_insert(0);
// 计数:entry API 的经典用法
let text = "the quick brown fox jumps over the lazy dog the end";
let mut counts = HashMap::new();
for word in text.split_whitespace() {
*counts.entry(word).or_insert(0) += 1;
}
let mut pairs: Vec<_> = counts.iter().collect();
pairs.sort_by(|a, b| b.1.cmp(a.1).then(a.0.cmp(b.0)));
for (w, c) in pairs.iter().take(3) {
println!("{} {}", w, c);
}
}Output
Some(10)
the 3
brown 1
dog 1| 方法 | 行为 |
|---|---|
insert | 插入,返回旧值 |
get / get_mut | 取引用 |
entry(k).or_insert(v) | 不存在则插入,返回 &mut V |
entry(k).or_default() | 不存在则用 Default |
remove | 删除并返回 |
contains_key | 是否存在 |
entry 只做一次哈希查找。写成 if !m.contains_key(k) { m.insert(k, 0); } *m.get_mut(k).unwrap() += 1 是三次查找,慢且啰嗦。
HashSet
Rust
use std::collections::HashSet;
fn main() {
let a: HashSet<i32> = [1, 2, 3, 4].into_iter().collect();
let b: HashSet<i32> = [3, 4, 5, 6].into_iter().collect();
let mut inter: Vec<_> = a.intersection(&b).copied().collect();
inter.sort();
println!("交集 {:?}", inter);
let mut uni: Vec<_> = a.union(&b).copied().collect();
uni.sort();
println!("并集 {:?}", uni);
let mut diff: Vec<_> = a.difference(&b).copied().collect();
diff.sort();
println!("差集 {:?}", diff);
// 去重
let v = vec![1, 1, 2, 3, 3];
let uniq: HashSet<_> = v.iter().collect();
println!("{}", uniq.len());
}Output
交集 [3, 4]
并集 [1, 2, 3, 4, 5, 6]
差集 [1, 2]
3String
Rust
fn main() {
let mut s = String::from("Hello");
s.push_str(", world");
s.push('!');
println!("{}", s);
// 拼接
let a = String::from("foo");
let b = String::from("bar");
let c = format!("{}{}", a, b);
println!("{}", c);
println!("{} {}", a, b); // format! 不移动
let d = a + &b; // + 会移动 a
println!("{}", d);
// 常用
println!("{}", s.to_uppercase());
println!("{}", s.replace("world", "Rust"));
println!("{}", s.contains("Hello"));
println!("{}", s.len()); // 字节数
println!("{}", s.chars().count()); // 字符数
}Output
Hello, world!
foobar
foo bar
foobar
HELLO, WORLD!
Hello, Rust!
true
13
13a + &b 会**移动 a**,因为 Add 的实现是 fn add(self, other: &str)。想保留 a 就用 format! 或 a.clone() + &b。这是 Rust 新手最常遇到的「为什么 a 不能用了」。字符串与字符
Rust
fn main() {
let s = "Hello, 世界";
// 按字符
for c in s.chars() {
print!("{} ", c);
}
println!();
// 按字节
println!("字节数 {}", s.len());
println!("字符数 {}", s.chars().count());
// 反转(按字符,不是字节)
let rev: String = s.chars().rev().collect();
println!("{}", rev);
// 按分隔符拆
for part in "a,b,c".split(',') {
print!("{} ", part);
}
println!();
}Output
H e l l o , 世 界
字节数 13
字符数 9
界世 ,olleH
a b c s.len() 是 13(7 个 ASCII + 逗号空格 + 两个汉字各 3 字节),chars().count() 是 9。这两个数不一致,是 Rust 字符串最容易搞错的地方。