Unsafe Rust - Wyatt's Notes
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.