关于 rust-unsafe
rust-unsafe 帮助 Agent 编写、审查和分析 Rust unsafe 代码,讲解不变量、安全抽象、指针规则和审计检查。
主要功能
- 01五种 unsafe 操作能力
- 02原始指针安全规则
- 03基于 unsafe 的安全抽象
- 04transmute 与 UnsafeCell 指南
- 05unsafe 审计清单
适用场景
- 01审查 unsafe 代码块
- 02为原始指针设计安全封装
- 03检查 unsafe trait 实现
- 04判断是否需要使用 unsafe
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ mohitmishra786/ low-level-dev-skills --skill rust-unsafe安装后在 Claude Code 中输入/rust-unsafe确认已加载。
附带文件references/unsafe-patterns.md
详细介绍
Rust unsafe
用途
指导 Agent 编写、审查和分析 unsafe Rust:哪些操作需要 unsafe、如何编写安全抽象、审计模式、常见陷阱,以及何时使用 unsafe。
触发条件
- “在 Rust 中何时需要使用 unsafe?”
- “如何基于 unsafe 代码编写安全抽象?”
- “如何审计 unsafe 代码块?”
- “Rust 中原始指针有哪些规则?”
- “transmute 的作用是什么,何时安全?”
- “如何正确实现 UnsafeCell?”
工作流
1. unsafe 的五种超能力
unsafe 恰好提供了安全 Rust 中不可用的五种能力:
- 解引用原始指针(
*const T、*mut T) - 调用 unsafe 函数(包括
extern "C"函数) - 访问或修改可变静态变量
- 实现 unsafe trait(
Send、Sync) - 访问联合体字段
Rust 中的其他一切操作——包括内存分配、借用和闭包——即使在 unsafe 代码块中也遵循安全规则。
2. 原始指针
// Creating raw pointers (safe — no dereference yet)
let x = 42u32;
let ptr: *const u32 = &x;
let mut_ptr: *mut u32 = &mut some_val as *mut u32;
// Null pointer
let null: *const u32 = std::ptr::null();
let null_mut: *mut u32 = std::ptr::null_mut();
// Dereference (unsafe)
let val = unsafe { *ptr };
// Null check
if !ptr.is_null() {
let val = unsafe { *ptr };
}
// Offset (safe to compute, unsafe to dereference)
let arr = [1u32, 2, 3, 4, 5];
let p = arr.as_ptr();
let third = unsafe { *p.add(2) }; // arr[2]
let also_third = unsafe { *p.offset(2) };
// Slice from raw parts
let slice: &[u32] = unsafe {
std::slice::from_raw_parts(p, arr.len())
};
正确解引用原始指针的规则:
- 指针必须非空
- 指针必须满足
T的对齐要求 - 内存必须已初始化为
T - 不得违反别名规则(一个位置只能有一个
&mut) - 内存必须在引用的生命周期内有效
3. unsafe 函数和 trait
// Declare unsafe function (callers must uphold invariants)
/// # Safety
/// `ptr` must be non-null and aligned to `T`, and point to initialized data.
/// The caller must ensure no other mutable reference to the same location exists.
unsafe fn read_ptr<T>(ptr: *const T) -> T {
ptr.read() // ptr::read is unsafe
}
// Call unsafe function
let val = unsafe { read_ptr(some_ptr) };
// Unsafe trait — implementor must uphold safety invariants
unsafe trait MyUnsafeTrait {
fn operation(&self);
}
// Implementing an unsafe trait is unsafe
unsafe impl MyUnsafeTrait for MyType {
fn operation(&self) { /* must uphold the trait's invariants */ }
}
// Send and Sync
// Send: type can be moved to another thread
// Sync: type can be shared between threads (&T is Send)
unsafe impl Send for MyType {}
unsafe impl Sync for MyType {}
4. 基于 unsafe 编写安全抽象
// The golden rule: unsafe blocks should be small, isolated, and
// wrapped in a safe API that maintains the invariant
pub struct MyVec<T> {
ptr: *mut T,
len: usize,
cap: usize,
}
impl<T> MyVec<T> {
pub fn new() -> Self {
MyVec { ptr: std::ptr::NonNull::dangling().as_ptr(), len: 0, cap: 0 }
}
// Safe public API
pub fn get(&self, index: usize) -> Option<&T> {
if index < self.len {
// Safety: index < len guarantees ptr+index is in bounds and initialized
Some(unsafe { &*self.ptr.add(index) })
} else {
None
}
}
// # Safety comment documents the invariant
pub fn push(&mut self, val: T) {
if self.len == self.cap {
self.grow();
}
// Safety: len < cap after grow(), so ptr+len is in bounds
unsafe { self.ptr.add(self.len).write(val) };
self.len += 1;
}
}
// Implement Drop to clean up
impl<T> Drop for MyVec<T> {
fn drop(&mut self) {
// Safety: ptr was allocated with this layout, and all elements are initialized
unsafe {
std::ptr::drop_in_place(std::slice::from_raw_parts_mut(self.ptr, self.len));
std::alloc::dealloc(self.ptr as *mut u8,
std::alloc::Layout::array::<T>(self.cap).unwrap());
}
}
}
5. transmute
// transmute: reinterpret bits of one type as another
// Both types must have the same size
// Safe uses:
let x: u32 = 0x3f800000;
let f: f32 = unsafe { std::mem::transmute(x) }; // bits → float
// Transmute slice pointer (sound if types have same size/align)
let bytes: &[u8] = &[0x00, 0x00, 0x80, 0x3f];
let floats: &[f32] = unsafe {
std::slice::from_raw_parts(bytes.as_ptr() as *const f32, 1)
};
// Prefer safe alternatives when available:
let f = f32::from_bits(x); // instead of transmute for float bits
let n = u32::from_ne_bytes(bytes); // instead of transmute for byte arrays
transmute 常见陷阱:
- 大小不匹配(会导致编译错误,但泛型类型需要特别检查)
- 创建无效的枚举值
- 创建生命周期不正确的引用
6. UnsafeCell — 内部可变性
use std::cell::UnsafeCell;
// UnsafeCell is the only way to mutate through a shared reference
struct MyCell<T> {
value: UnsafeCell<T>,
}
impl<T: Copy> MyCell<T> {
fn new(val: T) -> Self {
MyCell { value: UnsafeCell::new(val) }
}
fn get(&self) -> T {
// Safety: single-threaded, no concurrent mutation
unsafe { *self.value.get() }
}
fn set(&self, val: T) {
// Safety: single-threaded, no outstanding references
unsafe { *self.value.get() = val }
}
}
7. unsafe 审计检查清单
审查 unsafe 代码块时:
- 是否有
// Safety:注释说明不变量? - 原始指针是否非空?
- 原始指针是否与目标类型正确对齐?
- 内存是否已初始化?
- 引用的生命周期是否有效?
- 是否遵守别名规则(没有同时存在
&和&mut)? - 对于
extern "C":是否记录并验证了 C 端不变量? - 对于
Send/Sync实现:是否确实保证了线程安全? - unsafe 代码块是否尽可能小?
- 是否为 unsafe 代码编写了 Miri 测试?
8. 何时使用 unsafe
Before reaching for unsafe, check:
├── Does std have a safe API? (Vec, Box, Arc — usually yes)
├── Does a crate handle it? (memmap2, nix, windows-sys)
├── Can you restructure to avoid it?
└── Is the performance gain measured and significant?
Legitimate uses:
├── FFI to C libraries (extern "C")
├── OS-level APIs (syscalls, mmap, ioctl)
├── Performance-critical data structures (custom allocators, SoA)
├── Hardware access (embedded, drivers)
└── Implementing safe abstractions (the standard library itself)
有关 unsafe 模式和审计示例,请参阅 references/unsafe-patterns.md。
相关技能
- 使用
skills/rust/rust-sanitizers-miri— Miri 是测试 unsafe 代码的必备工具 - 在 FFI 场景中使用 unsafe 模式,请参阅
skills/rust/rust-ffi - 调试 unsafe 代码中的 panic,请参阅
skills/rust/rust-debugging - 在 unsafe 代码中处理别名和内存排序,请参阅
skills/low-level-programming/memory-model
评论
支持 Markdown