系统编程

第16课:Collections

🎯 本课目标

掌握Vec、HashMap、HashSet、VecDeque、BTreeMap等集合类型的使用和性能特征。

1. Vec<T> — 动态数组

fn main() {
    let mut v: Vec<i32> = Vec::new();
    v.push(1); v.push(2); v.push(3);

    let v2 = vec![1, 2, 3, 4, 5];

    // 访问
    let third = &v2[2];            // 索引(越界panic)
    let third = v2.get(2);          // 安全访问(Option)

    // 遍历
    for i in &mut v { *i *= 2; }  // 可变遍历

    // 容量管理
    let mut v3 = Vec::with_capacity(100);
    println!("len={} cap={}", v3.len(), v3.capacity());
    v3.reserve(50);
    v3.shrink_to_fit();
}

✅ 编译验证通过

Vec内存模型: Vec<i32> 结构体(栈上24字节): ┌────────────┐ │ ptr ──────────→ 堆上连续内存 │ len: 5 │ │ cap: 8 │ └────────────┘ 堆上: ┌───┬───┬───┬───┬───┬───┬───┬───┐ │ 1 │ 2 │ 3 │ 4 │ 5 │ │ │ │ └───┴───┴───┴───┴───┴───┴───┴───┘ ← len=5 → ← 预留空间 → ←────── capacity=8 ─────→ push时:如果len==cap,重新分配2倍空间

2. HashMap

use std::collections::HashMap;

fn main() {
    let mut scores: HashMap<String, i32> = HashMap::new();
    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Red"), 50);

    // 访问
    let team = "Blue";
    if let Some(score) = scores.get(team) {
        println!("{}: {}", team, score);
    }

    // 遍历
    for (key, value) in &scores {
        println!("{}: {}", key, value);
    }

    // 只在键不存在时插入
    scores.entry(String::from("Yellow")).or_insert(25);

    // 更新值
    let text = "hello world wonderful world";
    let mut map = HashMap::new();
    for word in text.split_whitespace() {
        let count = map.entry(word).or_insert(0);
        *count += 1;
    }
}

✅ 编译验证通过

3. HashSet

use std::collections::HashSet;

fn main() {
    let a: HashSet<i32> = [1, 2, 3, 4].into();
    let b: HashSet<i32> = [3, 4, 5, 6].into();

    // 集合运算
    println!("交集: {:?}", a.intersection(&b).collect::<Vec<_>>());
    println!("并集: {:?}", a.union(&b).collect::<Vec<_>>());
    println!("差集: {:?}", a.difference(&b).collect::<Vec<_>>());
}

✅ 编译验证通过

4. VecDeque与BTreeMap

use std::collections::{VecDeque, BTreeMap};

fn main() {
    // 双端队列
    let mut dq = VecDeque::new();
    dq.push_back(1);
    dq.push_front(2);
    let front = dq.pop_front();
    let back = dq.pop_back();

    // 有序映射
    let mut map = BTreeMap::new();
    map.insert(3, "c");
    map.insert(1, "a");
    map.insert(2, "b");
    // 遍历自动按key排序
    for (k, v) in &map {
        println!("{}: {}", k, v);  // 1:a, 2:b, 3:c
    }
    // 范围查询
    for (k, v) in map.range(2..=3) {
        println!("range {}: {}", k, v);
    }
}

✅ 编译验证通过

5. 集合性能对比

集合查找插入删除有序
VecO(n)O(1)尾/O(n)中O(n)插入序
HashMapO(1)均O(1)均O(1)均
BTreeMapO(log n)O(log n)O(log n)key序
VecDequeO(n)O(1)首尾O(1)首尾插入序
HashSetO(1)均O(1)均O(1)均

📝 练习题

练习1:LRU缓存

用HashMap+VecDeque实现LRU缓存。

练习2:词频统计

读取文本,统计Top-N高频词。

练习3:并查集

用HashMap实现Union-Find数据结构。

🏆

成就解锁:集合管家

掌握Rust集合类型——选择正确的数据结构

🔬 深度理解:Rust与系统编程

系统编程的核心挑战是在不牺牲安全的前提下获得最大性能。Rust如何实现这个目标:

1. 无GC的内存管理

所有权系统在编译期确定每个值的生命周期,不需要运行时垃圾回收。这意味着:无停顿、可预测延迟、更少内存占用。

2. 零成本抽象到硬件

泛型单态化、内联、LLVM优化——高级代码编译为与手写C相当的机器码。

3. 并发无数据竞争

Send/Sync trait在编译期保证:如果是并发访问,要么只读,要么互斥。数据竞争不可能发生。

📊 Rust vs C/C++对比

特性RustCC++
内存安全✅ 编译期❌ 手动⚠️ 部分RAII
线程安全✅ 编译期
无GC
零成本抽象⚠️ 宏✅ 模板
包管理✅ Cargo⚠️ vcpkg
学习曲线陡峭中等极陡

🛡️ 安全编码实践

即使Rust保证内存安全和线程安全,仍需注意:

Rust系统编程课程 · 第16课 · 2026

📚 扩展阅读

以下资源帮助你深入学习本课主题:

  • The Rust Programming Language — 官方教程,Rust圣经
  • Rust by Example — 通过示例学习Rust
  • Rust Reference — 语言参考手册
  • Rustnomicon — Unsafe Rust黑魔法指南
  • Too Many Lists — 用Rust实现各种链表

🔑 关键术语回顾

本课涉及的核心概念,确保你理解每一个:

术语说明
所有权Rust内存管理的核心机制,每个值有唯一所有者
借用通过引用访问数据,不获取所有权
生命周期引用有效的范围,编译期分析工具
traitRust的接口/抽象机制,类似其他语言的接口
泛型参数化类型,零成本抽象
模式匹配强大的数据解构和分支机制
零成本抽象高层抽象不引入运行时开销
fearless concurrency编译器保证线程安全

💬 学习建议

  1. 动手编码 — 每个代码示例都在本地运行一遍
  2. 修改实验 — 故意改错代码,看编译器报什么错
  3. 完成练习 — 每课的练习题是巩固知识的关键
  4. 阅读源码 — 看标准库和优秀开源项目的实现
  5. 写项目 — 真正掌握需要构建真实项目

🤔 常见问题

Q: Rust学习曲线真的很陡吗?

A: 前期确实需要适应所有权和借用检查器,但一旦理解了,这些概念会让你的代码更可靠。大多数人2-4周就能上手。

Q: Rust适合什么项目?

A: 系统编程、Web服务、CLI工具、嵌入式、WASM、网络服务、数据库等。基本上需要性能和安全的地方都适合。

Q: 遇到编译错误怎么办?

A: Rust编译器的错误信息非常友好!仔细阅读,通常会指出问题所在和修复建议。也可以用cargo clippy获取更多提示。