Rust 集合与字符串

`Vec`、`HashMap`、`HashSet`、`String` 的常用操作与坑。

Intermediate
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
MethodBehaviour
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]
3

String

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
13
a + &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.

Friends & Partners