Rust 标准库详解

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
上一篇
下一篇