unsafe Rust
unsafe is a keyword that unlocks five extra abilities the compiler cannot verify — like dereferencing raw pointers — handing you responsibility for upholding the guarantees Rust normally checks for you.
Learn unsafe Rust in our free Rust course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.
Part of the free Rust course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
In this lesson you'll learn the five unsafe superpowers, work with raw pointers ( *const / *mut ), write and call an unsafe fn , and see how to keep unsafe code small and sound.
What You'll Learn in This Lesson
1️⃣ Raw Pointers
Unsafe grants exactly five superpowers; the most common is working with raw pointers , written *const T and *mut T . Unlike references, raw pointers are not tracked by the borrow checker. Creating a raw pointer is perfectly safe — only dereferencing one requires an unsafe block, because the compiler can't guarantee it points to valid data.
Inside unsafe , the type system and ordinary borrow rules are still enforced — what changes is that raw pointers, which the checker simply ignores, become dereferenceable. Unsafe is not an "anything goes" mode.
2️⃣ Unsafe Functions
Marking a function unsafe fn declares that calling it carries obligations the compiler can't check — the caller must satisfy a documented contract. Every call site must therefore appear inside an unsafe block, which is the caller saying "I've read the rules and I'm upholding them."
Always pair an unsafe fn with a // Safety: comment stating exactly what the caller must guarantee. This is the same pattern the standard library uses for functions like get_unchecked .
3️⃣ Wrapping Unsafe in a Safe API
The hallmark of good unsafe code is that it is encapsulated : a small, audited unsafe core sits behind a safe function that upholds the necessary invariants, so callers never touch unsafe at all. The standard library's split_at_mut is the textbook case — it returns two non-overlapping mutable slices, something safe Rust can't prove on its own.
The assert!(mid <= len) guarantees the precondition before any pointer math, and the two ranges provably never overlap — so this safe wrapper can never be misused into undefined behavior. That is the standard you should hold all unsafe code to.
Your turn. Fill in the blank marked ___ , then run it.
Use a single, sound unsafe block to increment a value through a mutable raw pointer. Run it with cargo run and check the output.
📋 Quick Reference — unsafe
Practice quiz
How many extra abilities does unsafe Rust unlock?
- One
- Three
- Five superpowers
- Unlimited
Answer: Five superpowers. Unsafe unlocks exactly five operations the compiler cannot verify.
Which operation requires an unsafe block?
- Dereferencing a raw pointer
- Creating a raw pointer
- Declaring a struct
- Calling a safe function
Answer: Dereferencing a raw pointer. Making a raw pointer is safe; only dereferencing it needs unsafe.
Is CREATING a raw pointer like &x as *const i32 unsafe?
- Yes, always unsafe
- Only for *mut
- Only inside a function
- No, creating it is safe
Answer: No, creating it is safe. Creating raw pointers is safe; dereferencing them is what requires unsafe.
What does an unsafe fn declare about its caller?
- The function never returns
- The caller must uphold a documented contract
- The function is faster
- The function is private
Answer: The caller must uphold a documented contract. Calling an unsafe fn must happen in an unsafe block where the caller upholds the rules.
Does unsafe turn off the borrow checker for normal references?
- No, references are still checked
- Yes, completely
- Only for &mut
- Only in main
Answer: No, references are still checked. Ordinary references are still checked; only raw pointers escape the borrow checker.
Which is NOT one of the five unsafe superpowers?
- Dereferencing a raw pointer
- Calling an unsafe function
- Ignoring the type system
- Accessing a mutable static
Answer: Ignoring the type system. Unsafe never disables the type system; it only enables those specific operations.
How is an immutable raw pointer written?
- *mut T
- *const T
- &T
- ref T
Answer: *const T. *const T is an immutable raw pointer; *mut T is the mutable form.
What is the recommended way to keep unsafe code sound?
- Make unsafe blocks as large as possible
- Avoid all comments
- Skip bounds checks everywhere
- Wrap a small unsafe core behind a safe API
Answer: Wrap a small unsafe core behind a safe API. Keep unsafe blocks tiny and expose a checked, safe wrapper like split_at_mut does.
Why does the safe split_at_mut use unsafe internally?
- It is faster to write
- Safe Rust can't prove two mutable slices don't overlap
- It avoids the type system
- Slices require unsafe
Answer: Safe Rust can't prove two mutable slices don't overlap. The borrow checker can't verify the halves are non-overlapping, so unsafe is used soundly.
What should accompany each unsafe block as good practice?
- A panic
- A println
- A // Safety: comment stating the invariants
- A lifetime annotation
Answer: A // Safety: comment stating the invariants. Document exactly which invariants make the unsafe block sound.