Unsafe Rust
What unsafe Enables
Section titled “What unsafe Enables”The unsafe keyword grants access to five capabilities that the compiler cannot verify:
- Dereference raw pointers,
*const Tand*mut T - Call unsafe functions,
fn foo() { unsafe { ... } } - Access mutable statics,
static mut X: i32 - Implement unsafe traits,
unsafe impl Send for T {} - Access union fields. Unions require unsafe for field access
unsafe does not disable the borrow checker. It does not bypass Rust’s safety guarantees, it Allows you to do things that the compiler cannot prove are safe. You are responsible for maintaining all invariants manually.
Raw Pointers
Section titled “Raw Pointers”*const T and *mut T
Section titled “*const T and *mut T”Raw pointers are like C pointers, they can be null, dangling, misaligned, or aliased. The compiler does not check them:
let x = 42;let raw_const: *const i32 = &x;let mut y = 42;let raw_mut: *mut i32 = &mut y;
unsafe { println!("const: {}", *raw_const); println!("mut: {}", *raw_mut); *raw_mut = 43; assert_eq!(*raw_mut, 43);}Creating Raw Pointers
Section titled “Creating Raw Pointers”Raw pointers can be created in safe code, only dereferencing them requires unsafe:
let x = 42;let ptr: *const i32 = &x; // safelet ptr_mut: *mut i32 = &mut x; // safelet null: *const i32 = std::ptr::null(); // safe
unsafe { // *ptr // only dereferencing is unsafe}Raw Pointer Operations
Section titled “Raw Pointer Operations”let mut values = [1i32, 2, 3, 4, 5];let ptr: *mut i32 = values.as_mut_ptr();
unsafe { // Offset, returns pointer to ptr + count let second = ptr.add(1); assert_eq!(*second, 2);
// Read without moving let val = ptr.read(); assert_eq!(val, 1);
// Write ptr.write(100); assert_eq!(values[0], 100);
// Read-add-write in one operation let old = ptr.replace(200); assert_eq!(old, 100); assert_eq!(*ptr, 200);}as_ref and as_mut
Section titled “as_ref and as_mut”Convert raw pointers to optional references:
let x = 42;let ptr: *const i32 = &x;
let reference: Option<&i32> = unsafe { ptr.as_ref() };assert_eq!(reference, Some(&42));
let null: *const i32 = std::ptr::null();let reference: Option<&i32> = unsafe { null.as_ref() };assert_eq!(reference, None);as_ref() returns None for null pointers, preventing undefined behavior from null dereferences.
Pointer Arithmetic
Section titled “Pointer Arithmetic”let mut arr = [10i32, 20, 30, 40, 50];let ptr = arr.as_mut_ptr();
unsafe { for i in 0..arr.len() { *ptr.add(i) *= 2; }}assert_eq!(arr, [20, 40, 60, 80, 100]);Intuition
Section titled “Intuition”Unsafe Rust is the escape hatch from the borrow checker’s safety guarantees. Think of it as signing a contract: the compiler says “I cannot verify this is safe, so you are responsible for保证ing it.” You get five superpowers: dereferencing raw pointers, calling unsafe functions, accessing mutable statics, implementing unsafe traits, and accessing union fields. The key insight is that unsafe does not disable the borrow checker; it directly lets you do things the checker cannot prove are safe. Most Rust code is safe; unsafe is reserved for FFI, low-level optimisations, and building safe abstractions.