Rust 标准库(std)字典:按模块速查高频类型与函数。每条给模块定位、核心 API、示例与坑。
1. std::collections – 集合
描述
标准库四种集合:HashMap(哈希无序)、BTreeMap(红黑树有序)、VecDeque(双端队列)、BinaryHeap(大顶堆);另有 HashSet/BTreeSet。
语法和示例
use std::collections::{HashMap, BTreeMap, VecDeque, BinaryHeap, HashSet};
let mut m: HashMap<String, i32> = HashMap::new();
*m.entry("a".into()).or_insert(0) += 5; // entry 计数惯用法
m.entry("b".into()).and_modify(|v| *v += 1).or_insert(1);
m.get("a"); m.contains_key("a"); m.remove("a");
let sorted: BTreeMap<String, i32> = BTreeMap::new(); // 有序
let mut dq: VecDeque<i32> = VecDeque::new();
dq.push_back(1); dq.push_front(0); dq.pop_front();
let mut heap = BinaryHeap::new(); // 大顶堆
heap.push(3); heap.peek(); heap.pop();
let set: HashSet<i32> = [1, 2].into_iter().collect();注意事项
- HashMap 默认用 SipHash(防哈希洪泛),性能不如 fxhash/ahash——热点路径换 hasher
- entry API 是唯一高效「缺则插入」方式,别先 contains 再 insert(两次查找)
- BTreeMap 要求键 Ord;HashMap 要求 Hash + Eq
- BinaryHeap 是大顶堆,小顶堆要包 Reverse 或自定义 Ord
- VecDeque 当队列;普通 Vec 的 remove(0) 是 O(n)
2. std::vec::Vec – 动态数组
描述
最重要的容器。push/pop 尾操作 O(1) 均摊;insert/remove 中间 O(n);capacity 与 len 是两回事。
语法和示例
let mut v = Vec::with_capacity(16); // 预分配避免扩容
v.push(1);
v.insert(0, 0);
v.pop();
v.len(); v.is_empty(); v.capacity();
v.contains(&1);
v.retain(|&x| x > 0); // 原地过滤
v.sort(); v.dedup(); // dedup 需先排序
v.iter().max(); v.iter().sum::<i32>();
v.chunks(100); // 分批
v.windows(2); // 滑动窗口
let first = v.first(); v.last();
v.truncate(3); v.clear();
// 索引越界 panic(安全但会崩),get 返回 Option
v.get(10); // None 而非 panic注意事项
- v[i] 越界会 panic;不确定时用 v.get(i) 返回 Option
- capacity 只在扩容时变,shrink_to_fit 可释放多余内存
- dedup 只去相邻重复,必须先 sort
- into_iter 消费所有权,iter 借用,iter_mut 可变借用
- Vec 每个元素占 1 字节(不是 1 bit),要位图用 bitvec crate
3. std::string – String 与 &str
描述
String 堆上可增长 UTF-8 字符串(拥有所有权);&str 借用的字符串切片。len() 是字节数不是字符数。
语法和示例
let mut s = String::with_capacity(64);
s.push_str("a"); s.push('b');
s += "tail";
let slice: &str = &s;
s.len(); // 字节数
s.chars().count(); // 字符数
s.is_empty(); s.contains("a");
s.replace("a", "b"); s.trim();
s.split(',').collect::<Vec<_>>();
s.to_uppercase(); s.to_lowercase();
let n: i32 = "42".parse().unwrap();
format!("{}-{}", a, b);
// 切片按字节(必须在字符边界,否则 panic)
&s[0..2];
// 转字节
s.as_bytes(); s.into_bytes();注意事项
- len() 是字节数:中文一个字 3 字节,String::from(“中”).len() == 3
- 按字节切片 &s[0..n] 切在字符中间会 panic——用 char_indices 找边界
- String 不能索引(s[0] 编译错),因为 UTF-8 不是定长
- push_str 与 push 不同(一个字符串一个字符)
- &str 是胖指针(指针 + 长度),不是简单引用
4. std::fmt – 格式化与 Display/Debug
描述
格式化 trait:Display({} 用户可读)、Debug({:?} 调试);宽度/精度/对齐/命名参数丰富。
语法和示例
println!("{}", s); // Display(需实现)
println!("{:?}", v); // Debug(可 derive)
println!("{:#?}", v); // 美化 Debug(多行)
println!("{:08.3}", 3.14); // 0003.140(补零 + 精度)
println!("{:<8}|", "a"); // 左对齐
println!("{:>8}|", "a"); // 右对齐
println!("{:^8}|", "a"); // 居中
println!("{name}={x:?}", name = "n", x = v);
eprintln!("错误"); // stderr
write!(f, "...")?; // 自定义 Display
// 自定义实现
impl std::fmt::Display for Point {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}注意事项
- println! 需要类型实现 Display 或 Debug,自定义 struct 默认都没有
- struct 想用 {} 必须手写 Display(Debug 可 derive)
- 格式化参数会被借用,不会移动所有权
- write! 返回 Result,内部要 ? 传播(Display 实现里必须)
- format! 返回 String(有分配),println 直接输出无分配
5. std::option / std::result – 组合子链
描述
两个核心枚举的组合子:map/and_then/filter/or_else(Option);map/map_err/and_then(Result)。
语法和示例
opt.map(|x| x + 1)
.filter(|x| *x > 2)
.unwrap_or(0);
opt.and_then(|x| checked(x)); // 返回 Option 的链
opt.or_else(|| fallback());
opt.ok_or("缺值")?; // Option → Result
res.map(|v| v.len())
.map_err(|e| AppError::from(e))
.unwrap_or_default();
res.and_then(|v| validate(v));
// 组合两个 Option
a.zip(b); // 两者都有才 Some((a,b))
// 判断
opt.is_some_and(|x| x > 0); // 1.70+注意事项
- unwrap_or 的默认值是急切求值(有开销时用 unwrap_or_else)
- and_then 与 map 的区别:闭包返回 Option 时用 and_then(否则嵌套)
- ? 在 Option 上下文里会丢弃 Err 的原因——需要原因时先转 Result
- ok_or 每次构造错误(unwrap 场景用 ok_or_else 惰性)
- 组合子链过长会难读,超过四五层考虑拆分或 match
6. std::iter – 迭代器适配器
描述
惰性适配器(map/filter/take/skip/chain/zip/flat_map)与消费者(collect/sum/fold/count/for_each)。零成本抽象。
语法和示例
(0..10).filter(|x| x % 2 == 0)
.map(|x| x * x)
.take(3)
.collect::<Vec<_>>();
let sum: i32 = [1,2,3].iter().sum();
let zipped: Vec<_> = a.iter().zip(b.iter()).collect();
let flat: Vec<_> = nested.iter().flatten().collect();
xs.iter().fold(0, |acc, x| acc + x);
xs.iter().enumerate().map(|(i, x)| i + x).sum::<usize>();
xs.iter().position(|x| *x == target);
xs.iter().any(|x| x > 0); xs.iter().all(|x| x >= 0);
xs.iter().max_by_key(|x| x.score);
// 分组与去重
xs.iter().dedup_by(|a, b| a.id == b.id);
// 收集到 HashMap
pairs.into_iter().collect::<HashMap<_, _>>();注意事项
- 适配器惰性——不消费就什么都不做(常见困惑)
- collect 必须能推断目标类型,否则用 turbofish ::>()
- iter() 借引用,into_iter() 消费,iter_mut() 可变借用——方法可用性不同
- flat_map 是 map + flatten 的组合,返回迭代器时要 flatten
- 链式太长会影响编译错误可读性,可分段加 let
7. std::fs / std::path – 文件与路径
描述
fs 模块读写文件与目录;path 的 Path/PathBuf 处理路径(Path 是 &str 的包装,PathBuf 是拥有的)。
语法和示例
use std::{fs, path::{Path, PathBuf}};
fs::read_to_string("a.txt")?;
fs::write("a.txt", "内容")?;
fs::read("bin")?; // Vec<u8>
fs::create_dir_all("x/y")?;
fs::remove_file("a")?;
fs::remove_dir_all("x")?;
fs::read_dir(".")?; // 迭代器,每项是 Result
let p = Path::new("data").join("a.log");
p.exists(); p.is_file(); p.is_dir();
p.extension(); p.file_stem(); p.parent();
p.to_string_lossy(); // 可能非 UTF-8 时用它
let pb: PathBuf = p.to_path_buf();
// 规范化
fs::canonicalize(p)?;注意事项
- read_dir 的每项是 Result,要 unwrap 或 ? 逐个处理
- Path 的 exists() 有 TOCTOU 竞态(检查后使用之间文件可能变)——重要场景直接操作并处理错误
- to_str() 返回 Option(非 UTF-8 路径会 None),展示用 to_string_lossy
- join 不检查存在性,只是拼接
- 跨平台路径分隔符差异由 Path 自动处理,别手拼字符串
8. std::time – Instant 与 Duration
描述
Instant 单调时钟(测耗时用,不受系统时间调整影响);Duration 时间量;SystemTime 是墙上时间。
语法和示例
use std::time::{Instant, Duration, SystemTime, UNIX_EPOCH};
let t0 = Instant::now();
// work...
let elapsed = t0.elapsed(); // Duration
elapsed.as_secs_f64(); elapsed.as_millis();
let d = Duration::from_millis(500);
std::thread::sleep(d);
Duration::from_secs(2); Duration::from_secs_f64(0.5);
d.as_secs() * 1000 + d.subsec_millis() as u64;
// 墙上时间
SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();注意事项
- 测耗时用 Instant 不用 SystemTime(后者会被 NTP 调整影响甚至倒退)
- Duration 的 as_secs 截断小数,要精度用 as_secs_f64 或 subsec_millis
- sleep 会让出线程但阻塞(async 里要用 tokio::time::sleep)
- Instant 不能序列化也不能跨进程比较(每个进程起点不同)
- Duration::from_secs_f64 对负数会 panic
9. std::thread / std::sync – 线程与同步
描述
thread::spawn 创建线程、join 等待;sync 提供 Mutex/RwLock/Arc/原子类型/OnceLock;mpsc 是通道。
语法和示例
use std::{thread, sync::{Arc, Mutex, mpsc, OnceLock}};
let h = thread::spawn(|| 42);
h.join().unwrap();
// 共享可变状态
let data = Arc::new(Mutex::new(Vec::new()));
let clone = Arc::clone(&data);
thread::spawn(move || { clone.lock().unwrap().push(1); });
// 通道
let (tx, rx) = mpsc::channel();
thread::spawn(move || tx.send(42).unwrap());
rx.recv().unwrap();
// scoped(1.63+):可借用栈数据
thread::scope(|s| { s.spawn(|| use_stack(&data2)); });
// 惰性全局
static CFG: OnceLock<String> = OnceLock::new();
CFG.get_or_init(|| load());
// 原子
use std::sync::atomic::{AtomicUsize, Ordering};
static HITS: AtomicUsize = AtomicUsize::new(0);
HITS.fetch_add(1, Ordering::Relaxed);注意事项
- Mutex lock 返回 Result(中毒状态)——unwrap 会在持锁线程 panic 后连锁崩
- Arc> 是线程共享可变状态的标准组合;Rc 只能单线程
- spawn 要求闭包 ‘static——借用外部数据用 thread::scope
- 不要跨 await 点持有 MutexGuard(async 里用 tokio::sync::Mutex)
- Ordering 选择影响性能与正确性:Relaxed 只保原子性,SeqCst 最强也最慢
10. std::process / std::env – 进程与环境
描述
读命令行参数、环境变量、子进程执行、退出码。
语法和示例
use std::process::{Command, exit};
let args: Vec<String> = std::env::args().collect(); // args[0] 是程序名
let home = std::env::var("HOME").unwrap_or_default();
std::env::set_var("KEY", "v"); // 1.80+ 变为 unsafe
let out = Command::new("git").args(["status", "--short"]).output()?;
String::from_utf8_lossy(&out.stdout);
// 流式
let mut child = Command::new("ping").arg("-c").arg("4").spawn()?;
child.wait()?;
exit(1);
// 当前目录
std::env::current_dir()?;注意事项
- args() 第一个是程序路径(不是第一个参数),解析参数要 skip(1)
- env::set_var 在多线程环境不安全(1.80+ 标记为 unsafe)
- Command 不经过 shell——管道/通配符要显式 sh -c
- output() 会等进程结束并缓存全部输出;大输出用 spawn + 手动读
- from_utf8_lossy 会把非 UTF-8 转成含替换字符的字符串,要精确用 bytes
11. 智能指针:Box / Rc / Arc / RefCell / Cow
描述
Box 堆分配唯一所有权;Rc 单线程共享计数;Arc 原子计数跨线程;RefCell 运行时借用检查;Cow 写时克隆。
语法和示例
use std::{boxed::Box, rc::Rc, cell::RefCell, borrow::Cow, sync::Arc};
let b = Box::new(5);
// 递归类型必须 Box
enum List { Nil, Cons(i32, Box<List>) }
// 单线程共享可变
let shared = Rc::new(RefCell::new(vec![1]));
shared.borrow_mut().push(2);
// 跨线程
let arc = Arc::new(Mutex::new(0));
// 写时克隆
let cow: Cow<str> = Cow::Borrowed("hi");
let owned = cow.into_owned();注意事项
- Rc 不是线程安全(计数非原子),跨线程必须 Arc
- RefCell 的借用规则在运行时检查——违规会 panic 而不是编译错
- Rc/Arc 形成环会内存泄漏——用 Weak 打破
- Box 与 impl Trait 的选择:需要动态分发放集合里时用前者
- Cow 适合「多数情况只读」的场景,避免无谓 clone