| name | rust-unsafe |
| description | Rust unsafe code skill for systems programming. Use when writing or reviewing unsafe Rust, understanding what operations require unsafe, implementing safe abstractions over unsafe code, auditing unsafe blocks, or understanding raw pointers, transmute, and extern. Activates on queries about unsafe Rust, raw pointers, transmute, unsafe blocks, writing safe wrappers, UnsafeCell, unsafe trait impl, or auditing unsafe code. |
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.
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?"
Workflow
1. The five unsafe superpowers
unsafe grants exactly five capabilities not available in safe Rust:
- Dereference raw pointers (
*const T, *mut T)
- Call unsafe functions (including
extern "C" functions)
- 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 unsafe blocks.
2. Raw pointers
let x = 42u32;
let ptr: *const u32 = &x;
let mut_ptr: *mut u32 = &mut some_val as *mut u32;
let null: *const u32 = std::ptr::null();
let null_mut: *mut u32 = std::ptr::null_mut();
let val = unsafe { *ptr };
if !ptr.is_null() {
let val = unsafe { *ptr };
}
let arr = [1u32, 2, 3, 4, 5];
let p = arr.as_ptr();
let third = unsafe { *p.add(2) };
let also_third = unsafe { *p.offset(2) };
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
3. unsafe functions and traits
unsafe fn read_ptr<T>(ptr: *const T) -> T {
ptr.read()
}
let val = unsafe { read_ptr(some_ptr) };
unsafe trait MyUnsafeTrait {
fn operation(&self);
}
unsafe impl MyUnsafeTrait for MyType {
fn operation(&self) { }
}
unsafe impl Send for MyType {}
unsafe impl Sync for MyType {}
4. Safe abstractions over unsafe
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 }
}
pub fn get(&self, index: usize) -> Option<&T> {
if index < self.len {
Some(unsafe { &*self.ptr.add(index) })
} else {
None
}
}
pub fn push(&mut self, val: T) {
if self.len == self.cap {
self.grow();
}
{ .ptr.(.len).(val) };
.len += ;
}
}
<T> <T> {
(& ) {
{
std::ptr::(std::slice::(.ptr, .len));
std::alloc::(.ptr * ,
std::alloc::Layout::array::<T>(.cap).());
}
}
}
5. transmute
let x: u32 = 0x3f800000;
let f: f32 = unsafe { std::mem::transmute(x) };
let bytes: &[u8] = &[0x00, 0x00, 0x80, 0x3f];
let floats: &[f32] = unsafe {
std::slice::from_raw_parts(bytes.as_ptr() as *const f32, 1)
};
let f = f32::from_bits(x);
let n = u32::from_ne_bytes(bytes);
Common transmute pitfalls:
- Wrong sizes (compile error, but check for generic types)
- Creating invalid enum values
- Creating references with wrong lifetimes
6. UnsafeCell — interior mutability
use std::cell::UnsafeCell;
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 {
unsafe { *self.value.get() }
}
fn set(&self, val: T) {
unsafe { *self.value.get() = val }
}
}
7. Unsafe audit checklist
When reviewing an unsafe block:
8. When to use 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.
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