rust-unsafe

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Rust unsafe

Rust unsafe

Purpose

用途

Guide agents through writing, reviewing, and reasoning about unsafe Rust: what operations require
unsafe
, how to write safe abstractions, audit patterns, common pitfalls, and when to reach for
unsafe
.
指导开发者编写、审阅和分析Rust Unsafe代码:包括哪些操作需要使用
unsafe
、如何编写安全抽象、审计模式、常见陷阱以及何时应该使用
unsafe

Triggers

触发场景

  • "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
grants exactly five capabilities not available in safe Rust:
  1. Dereference raw pointers (
    *const T
    ,
    *mut T
    )
  2. Call unsafe functions (including
    extern "C"
    functions)
  3. Access or modify mutable static variables
  4. Implement unsafe traits (
    Send
    ,
    Sync
    )
  5. Access fields of unions
Everything else in Rust — including memory allocation, borrowing, closures — follows safe rules even inside
unsafe
blocks.
unsafe
提供了安全Rust中不具备的五项能力:
  1. 解引用裸指针 (
    *const T
    ,
    *mut T
    )
  2. 调用Unsafe函数(包括
    extern "C"
    函数)
  3. 访问或修改可变静态变量
  4. 实现Unsafe trait (
    Send
    ,
    Sync
    )
  5. 访问联合体的字段
Rust中的其他所有操作——包括内存分配、借用、闭包——即使在
unsafe
块内也遵循安全规则。

2. 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
    &mut
    to a location)
  • 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 arrays
Common 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 arrays
transmute常见陷阱:
  • 类型大小不匹配(编译时会报错,但需注意泛型类型)
  • 创建无效的枚举值
  • 创建生命周期错误的引用

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
unsafe
block:
  • Is there a
    // Safety:
    comment explaining the invariant?
  • 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
    extern "C"
    : are C invariants documented and verified?
  • For
    Send
    /
    Sync
    impl: is thread safety actually guaranteed?
  • Is the unsafe block as small as possible?
  • Is there a test under Miri for the unsafe code?
审阅
unsafe
块时需检查:
  • 是否有
    // Safety:
    注释说明不变量?
  • 裸指针是否非空?
  • 裸指针是否与目标类型正确对齐?
  • 内存是否已初始化?
  • 引用的生命周期是否有效?
  • 是否遵守别名规则(不存在同时的
    &
    &mut
    引用)?
  • 对于
    extern "C"
    :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
    skills/rust/rust-sanitizers-miri
    — Miri is the essential tool for testing unsafe code
  • Use
    skills/rust/rust-ffi
    for unsafe patterns in FFI contexts
  • Use
    skills/rust/rust-debugging
    for debugging panics in unsafe code
  • Use
    skills/low-level-programming/memory-model
    for aliasing and memory ordering in unsafe
  • 使用
    skills/rust/rust-sanitizers-miri
    — Miri是测试Unsafe代码的必备工具
  • 使用
    skills/rust/rust-ffi
    了解FFI场景下的Unsafe模式
  • 使用
    skills/rust/rust-debugging
    调试Unsafe代码中的panic问题
  • 使用
    skills/low-level-programming/memory-model
    了解Unsafe代码中的别名和内存顺序