跳到主要内容

unsafe Rust

问题

unsafe 在 Rust 中意味着什么?什么时候需要使用 unsafe?

答案

unsafe 的 5 种超能力

unsafe 块/函数中,你可以做以下 5 件事:

  1. 解引用裸指针
  2. 调用 unsafe 函数
  3. 访问/修改可变静态变量
  4. 实现 unsafe trait
  5. 访问 union 的字段
// 1. 裸指针
let x = 42;
let ptr = &x as *const i32;
unsafe {
println!("{}", *ptr); // 解引用裸指针
}

// 2. 调用 unsafe 函数
unsafe fn dangerous() { /* ... */ }
unsafe { dangerous(); }

// 3. 可变静态变量
static mut COUNTER: u32 = 0;
unsafe { COUNTER += 1; }

// 4. unsafe trait
unsafe trait MyTrait { }
unsafe impl MyTrait for MyType { }

// 5. Union
union FloatBits {
f: f32,
bits: u32,
}
let u = FloatBits { f: 1.0 };
unsafe { println!("bits: {:#x}", u.bits); }

unsafe 不意味着"不安全"

unsafe 意味着"编译器无法验证这段代码的安全性,由程序员负责"。好的 unsafe 代码应该:

  1. 最小化 unsafe 范围
  2. 封装在安全抽象后面
  3. 详细文档说明安全不变量
// 标准库的 Vec::get_unchecked 就是安全封装的例子
pub fn get(&self, index: usize) -> Option<&T> {
if index < self.len {
Some(unsafe { self.get_unchecked(index) }) // unsafe 被安全 API 封装
} else {
None
}
}

常见的 unsafe 使用场景

FFI(外部函数接口):

extern "C" {
fn abs(input: i32) -> i32;
}

fn main() {
let result = unsafe { abs(-3) };
println!("{}", result); // 3
}

性能关键路径:

// 跳过边界检查(确认 index 合法时)
let slice = &[1, 2, 3, 4, 5];
let value = unsafe { *slice.get_unchecked(2) }; // 比 slice[2] 快(无边界检查)

未定义行为(UB)

以下是 Rust 中的 UB,即使在 unsafe 中也绝对不能做

  • 解引用空指针或悬垂指针
  • 创建未对齐的引用
  • 违反别名规则(同时存在可变和不可变引用)
  • 产生无效的原始值(如 bool 不是 0 或 1)
  • 数据竞争

常见面试问题

Q1: unsafe 和 C/C++ 的区别?

答案

在 C/C++ 中,所有代码都是"unsafe"的——编译器不保证内存安全。Rust 的 unsafe有限的、可审计的——你可以搜索所有 unsafe 块来审查安全性。安全代码中的 bug 不会导致未定义行为。

Q2: 什么是"安全抽象"?

答案

安全抽象是指用安全的 API 封装 unsafe 代码,使得外部用户无论怎么调用都不会触发未定义行为。标准库大量使用这种模式:VecStringHashMap 内部都有 unsafe,但对外 API 完全安全。

Q3: *const T*mut T 与引用有什么区别?

答案

特性&T / &mut T*const T / *mut T
空值不可能可能
对齐保证不保证
悬垂不可能可能
别名规则编译器检查不检查
解引用安全需要 unsafe

裸指针主要用于 FFI 和实现底层数据结构。

相关链接