unsafe-and-model
unsafe 与内存模型
unsafe 能做什么
- 解引用裸指针:
*const T/*mut T - 调用
unsafe fn或包含unsafe代码块 - 访问/修改可变静态变量:
static mut - 与外部(FFI)交互:
extern "C"、#[no_mangle] - 读写
union字段、手动管理布局/对齐(#[repr(C)]/align)
unsafe 只允许做“潜在不安全”的事,但不保证一定安全;正 确性取决于你维护的不变量(invariants)。
Rust 内存模型要点
- 别名与可变性:同一时刻“要么多个不可变引用,要么一个可变引用”
- 数据竞争即未定义行为(UB):两个或以上线程同时写/读写同一内存且无同步
- 对齐与有效性(validity/provenance):解引用必须满足对齐、生命周期、初始化等前提
- 原子语义:通过
std::sync::atomic指定内存序(Relaxed/Acquire/Release/AcqRel/SeqCst) - 线程安全由
Send/Sync决定:跨线程移动(Send)/共享(Sync)
参考:Rustonomicon、Unsafe Code Guidelines(UCG)
常见场景与范式
- FFI:与 C 交换指针/缓冲区、遵守对齐与所有权转移约定
- Zero-copy:
from_raw_parts/from_raw_parts_mut构造切片(需确保容量与对齐正确) - 自定义同步/容器:基于
UnsafeCell打破共享不可变,严格封装边界 - 内存映射与 DMA:对齐、缓存一致性、顺序保证需格外谨慎
最小示例(请勿在生产中裸用)
fn raw_demo() {
let mut v = vec![10, 20, 30];
let p = v.as_mut_ptr();
unsafe {
// 合法前提:p 指向的内存仍归 v 所有,未越界,且对齐
*p.add(1) = 99; // 修改第二个元素
}
assert_eq!(v[1], 99);
}
封装原则(关键)
- 将
unsafe尽可能封装在小函数内,向外暴露“完全安全”的 API - 明确写下不变量:对齐、越界、别名、生命周期、线程安全等前提条件
- 单元测试 +
Miri/Sanitizer/loom验证并发与内存访问
调试与验证工具
- Miri:
cargo +nightly miri test检查未定义行为 - Sanitizers:
ASan/TSan/UBSan(RUSTFLAGS配置或-Z sanitizer) loom:枚举并发交错以发现数据竞争