rust-unsafe
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRust unsafe
Rust unsafe
Purpose
用途
Guide agents through writing, reviewing, and reasoning about unsafe Rust: what operations require , how to write safe abstractions, audit patterns, common pitfalls, and when to reach for .
unsafeunsafe指导开发者编写、审阅和分析Rust Unsafe代码:包括哪些操作需要使用、如何编写安全抽象、审计模式、常见陷阱以及何时应该使用。
unsafeunsafeTriggers
触发场景
- "When do I need to use unsafe in Rust?"
- "How do I write a safe abstraction over unsafe code?"
- "How do I audit an unsafe block?"
- "What are the rules for raw pointers in Rust?"
- "What does transmute do and when is it safe?"
- "How do I implement UnsafeCell correctly?"
- "我在Rust中何时需要使用unsafe?"
- "如何基于Unsafe代码编写安全抽象?"
- "如何审计Unsafe代码块?"
- "Rust中裸指针的规则是什么?"
- "transmute的作用是什么,何时使用是安全的?"
- "如何正确实现UnsafeCell?"
Workflow
工作流程
1. The five unsafe superpowers
1. Unsafe的五大“超能力”
unsafe- Dereference raw pointers (,
*const T)*mut T - Call unsafe functions (including functions)
extern "C" - Access or modify mutable static variables
- Implement unsafe traits (,
Send)Sync - Access fields of unions
Everything else in Rust — including memory allocation, borrowing, closures — follows safe rules even inside blocks.
unsafeunsafe- 解引用裸指针 (,
*const T)*mut T - 调用Unsafe函数(包括函数)
extern "C" - 访问或修改可变静态变量
- 实现Unsafe trait (,
Send)Sync - 访问联合体的字段
Rust中的其他所有操作——包括内存分配、借用、闭包——即使在块内也遵循安全规则。
unsafe2. Raw pointers
2. 裸指针
rust
// 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())
};Rules for sound raw pointer dereference:
- Pointer must be non-null
- Pointer must be aligned for
T - Memory must be initialized for
T - Must not violate aliasing rules (only one to a location)
&mut - Memory must be valid for the lifetime of the reference
rust
// 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 functions and traits
3. Unsafe函数与Trait
rust
// 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 {}rust
// 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. Safe abstractions over unsafe
4. 基于Unsafe代码的安全抽象
rust
// 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());
}
}
}rust
// 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
5. transmute
rust
// 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 arraysCommon transmute pitfalls:
- Wrong sizes (compile error, but check for generic types)
- Creating invalid enum values
- Creating references with wrong lifetimes
rust
// 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 arraystransmute常见陷阱:
- 类型大小不匹配(编译时会报错,但需注意泛型类型)
- 创建无效的枚举值
- 创建生命周期错误的引用
6. UnsafeCell — interior mutability
6. UnsafeCell — 内部可变性
rust
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 }
}
}rust
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 audit checklist
7. Unsafe代码审计清单
When reviewing an block:
unsafe- Is there a comment explaining the invariant?
// Safety: - Is the raw pointer non-null?
- Is the raw pointer correctly aligned for the target type?
- Is the memory initialized?
- Is the lifetime of the reference valid?
- Are aliasing rules respected (no simultaneous and
&)?&mut - For : are C invariants documented and verified?
extern "C" - For /
Sendimpl: is thread safety actually guaranteed?Sync - Is the unsafe block as small as possible?
- Is there a test under Miri for the unsafe code?
审阅块时需检查:
unsafe- 是否有注释说明不变量?
// Safety: - 裸指针是否非空?
- 裸指针是否与目标类型正确对齐?
- 内存是否已初始化?
- 引用的生命周期是否有效?
- 是否遵守别名规则(不存在同时的和
&引用)?&mut - 对于:C语言的不变量是否已文档化并验证?
extern "C" - 对于/
Send实现:是否真的保证了线程安全?Sync - Unsafe块是否尽可能小?
- 是否使用Miri对Unsafe代码进行测试?
8. When to use unsafe
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)For unsafe patterns and audit examples, see references/unsafe-patterns.md.
在使用Unsafe之前,请先检查:
├── 标准库是否有安全API?(Vec、Box、Arc — 通常是有的)
├── 是否有第三方 crate 可以处理?(memmap2、nix、windows-sys)
├── 是否可以通过重构代码避免使用Unsafe?
└── 性能提升是否经过测量且显著?
合理的使用场景:
├── 与C库的FFI交互(extern "C")
├── 操作系统级API(系统调用、mmap、ioctl)
├── 性能关键的数据结构(自定义分配器、SoA)
├── 硬件访问(嵌入式、驱动程序)
└── 实现安全抽象(如标准库本身)有关Unsafe模式和审计示例,请参阅 references/unsafe-patterns.md。
Related skills
相关技能
- Use — Miri is the essential tool for testing unsafe code
skills/rust/rust-sanitizers-miri - Use for unsafe patterns in FFI contexts
skills/rust/rust-ffi - Use for debugging panics in unsafe code
skills/rust/rust-debugging - Use for aliasing and memory ordering in unsafe
skills/low-level-programming/memory-model
- 使用— Miri是测试Unsafe代码的必备工具
skills/rust/rust-sanitizers-miri - 使用了解FFI场景下的Unsafe模式
skills/rust/rust-ffi - 使用调试Unsafe代码中的panic问题
skills/rust/rust-debugging - 使用了解Unsafe代码中的别名和内存顺序
skills/low-level-programming/memory-model