Rust Unsafe
Every guarantee this course has talked about — no dangling references, no data races, no out-of-bounds access — is enforced by the compiler. unsafe is how you tell the compiler “let me do something you can’t verify is safe; I’m taking responsibility for it instead.”
What unsafe does — and doesn’t do
Section titled “What unsafe does — and doesn’t do”A common misconception is that unsafe turns off Rust’s safety checks entirely. It doesn’t. Inside an unsafe block, you can do exactly five extra things that are otherwise forbidden:
- Dereference a raw pointer
- Call an
unsafefunction - Access or modify a mutable
staticvariable - Implement an
unsafetrait - Access fields of a
union
Everything else — the borrowing rules, move semantics, type checking — still fully applies inside unsafe code. It’s a scalpel for five specific operations, not an off switch.
Raw pointers
Section titled “Raw pointers”Rust’s normal references (& and &mut) always point to valid, properly aligned data — that’s part of what the compiler guarantees. A raw pointer (*const T or *mut T) drops that guarantee, which is why reading one requires unsafe:
fn main() { let value = 5; let pointer = &value as *const i32;
unsafe { println!("{}", *pointer); }}Output:
5You can create a raw pointer in safe code — it’s only dereferencing it (reading the value it points to) that needs unsafe, since the compiler can no longer verify the pointer is valid.
Calling an unsafe function
Section titled “Calling an unsafe function”Some functions are themselves marked unsafe because they have requirements the compiler can’t check for you — calling them is your promise that you’ve verified those requirements yourself:
unsafe fn dangerous() { println!("this function trusts you to have checked its requirements");}
fn main() { unsafe { dangerous(); }}Output:
this function trusts you to have checked its requirementsWhy unsafe exists at all
Section titled “Why unsafe exists at all”If Rust is all about safety, why allow this at all? Because some things are genuinely impossible to prove safe at compile time, even though they’re perfectly safe in practice — talking directly to hardware, calling into a C library, or building certain high-performance data structures. unsafe lets the standard library (and, rarely, your own code) implement these low-level pieces, while everything built on top of them stays in ordinary, fully-checked safe Rust.
The honest tradeoff
Section titled “The honest tradeoff”Writing unsafe doesn’t mean your code is now buggy — it means the compiler can no longer catch certain classes of mistakes for you in that specific block, and it’s on you to uphold the same guarantees by hand. Most experienced Rust developers write very little unsafe code directly; it tends to live in small, carefully-reviewed pockets at the bottom of a library, wrapped in a safe API for everyone else to use.
unsafeunlocks exactly five operations (mainly raw pointers and calling unsafe functions) — everything else is still fully checked.- A raw pointer (
*const T/*mut T) can be created in safe code; dereferencing it requiresunsafe. unsafeexists so low-level code (hardware access, C interop) can be built, while code using it stays safe.- As a beginner, you’ll rarely need to write it yourself — reach for a safe abstraction first.
Quick check
Section titled “Quick check”Quick check
1. What does an unsafe block actually unlock?
2. Can you create a raw pointer in safe code?
3. As a beginner, how often should you expect to write unsafe yourself?
4. Does writing unsafe code mean your program now has bugs?
Score: 0 / 4