Rust unsafe 与裸指针使用边界
更新时间:2026-09-18。安全 Rust 已经能覆盖绝大多数业务;
unsafe只在和底层打交道时出现。理解它的边界比会写它更重要。
一、unsafe 到底开放了什么
unsafe 不会关闭借用检查器,安全代码的规则在 unsafe 块里依然生效。它只是解锁 5 类编译器无法替你验证的操作:
- 解引用裸指针
*const T/*mut T; - 调用
unsafe fn(如 FFI、部分底层 API); - 访问或修改可变静态变量
static mut; - 实现
unsafe trait; - 访问
union字段。
rust
let x = 5;
let p: *const i32 = &x as *const i32; // 创建裸指针是安全的
unsafe { println!("{}", *p); } // 只有「解引用」必须在 unsafe 中二、裸指针与引用的区别
| 特性 | &T / &mut T | *const T / *mut T |
|---|---|---|
| 借用检查 | 受约束(多读一写、非空、有效) | 不受约束,可空、可悬垂、可别名 |
| 解引用 | 安全 | 必须 unsafe |
| 用途 | 日常代码 | FFI、底层数据结构、与 C 交互 |
裸指针放弃了编译器安全保证,正确性责任完全转到你身上。
三、正确姿势:把 unsafe 关进安全 API
标准库大量内部用 unsafe,却对外提供安全接口。自己写也应如此:
rust
pub fn first_byte(s: &[u8]) -> Option<u8> {
if s.is_empty() { return None; }
// unsafe 块尽量小,并用注释写明它依赖的不变量
Some(unsafe { *s.as_ptr() }) // 已确保非空、长度≥1、指针有效
}原则:① unsafe 块最小化;② 用注释写清「为什么安全」(指针非空、有效、不别名等);③ 对外只暴露安全签名,让调用方无需 unsafe。
四、常见坑
- 以为 unsafe 能跳过借用检查:不能,违反规则的代码一样编译失败,unsafe 只解锁上面五类操作。
- FFI 类型不匹配:和 C 交互时
int/指针长度、字符串(CString的\0)、所有权释放方必须对齐,否则内存破坏。 static mut数据竞争:可变全局在多线程下是 UB,优先AtomicXxx或Mutex/lazy_static。- 在裸指针上做指针运算越界:Rust 不帮你检查边界,越界即未定义行为。
- 把 unsafe 当性能开关滥用:它主要用于能力而非提速;大部分性能优化用安全代码即可,参考本站性能观测工具链。
总结
**Rust 的 unsafe 不关闭借用检查,只是解锁解引用裸指针、调用 unsafe fn、访问 static mut、实现 unsafe trait、访问 union 这五类编译器无法验证的能力;裸指针 const/mut 可以为空、悬垂或别名,因此只有解引用放进 unsafe、正确性由开发者担保;工程上应让 unsafe 块最小化、注释写明依赖的不变量(非空、有效、不越界),并把它封装进对外的安全 API,FFI 注意类型与字符串和释放责任对齐,避免用 static mut 做多线程全局、不要把 unsafe 当通用提速手段。