← Back to Rust Course | Chapter 13: Smart Pointers & Unsafe | Lesson 6 of 7

Unsafe Basics

unsafe is a special zone where Rust trusts you to follow the safety rules yourself, since it can't check everything there.

What unsafe Unlocks

An unsafe block allows a small set of extra operations, such as dereferencing raw pointers or calling unsafe functions, that the compiler cannot fully verify are safe on its own.

Example: What unsafe Unlocks

markup
fn main() {
    let value = 10;
    let raw_pointer = &value as *const i32;
    unsafe {
        println!("Value through raw pointer: {}", *raw_pointer);
    }
}

Calling an Unsafe Function

Functions marked unsafe fn can only be called from inside an unsafe block, signaling that the caller must uphold certain safety guarantees the function relies on.

Example: Calling an Unsafe Function

markup
unsafe fn dangerous_operation() -> i32 {
    42
}

fn main() {
    let result = unsafe { dangerous_operation() };
    println!("Result: {}", result);
}

Safe Rust Still Applies Around unsafe

Code outside an unsafe block, and even most code inside it, still follows all of Rust's normal type checking and ownership rules -- unsafe only lifts a few specific restrictions.

Example: Safe Rust Still Applies Around unsafe

markup
fn main() {
    let mut value = 5;
    {
        let pointer = &mut value as *mut i32;
        unsafe {
            *pointer += 10;
        }
    }
    println!("value = {}", value);
}

Wrapping unsafe in a Safe API

A common pattern is to keep unsafe code small and private, exposing only a safe public function that guarantees its preconditions are met before using the unsafe operation internally.

Example: Wrapping unsafe in a Safe API

markup
fn safe_get(arr: &[i32], index: usize) -> Option<i32> {
    if index < arr.len() {
        Some(unsafe { *arr.get_unchecked(index) })
    } else {
        None
    }
}

fn main() {
    let data = [1, 2, 3];
    println!("{:?}", safe_get(&data, 1));
    println!("{:?}", safe_get(&data, 10));
}
Common Mistakes
  1. Reaching for unsafe to fix an ordinary borrow-checker error, when the real fix is almost always restructuring the safe code.
  2. Assuming unsafe turns off all of Rust's checks -- it only unlocks a small specific set of extra abilities, like dereferencing raw pointers.
  3. Writing a large unsafe block when only one specific line actually needs the extra capability, making it harder to audit.
Chapter Summary
  • unsafe blocks unlock a small set of extra abilities not allowed in safe Rust, such as dereferencing raw pointers.
  • Using unsafe does not disable the borrow checker or type checking elsewhere in the code.
  • Safe abstractions are often built around a small unsafe core, exposing a fully safe API to callers.
  • unsafe should be used sparingly and kept as small as possible to make manual review easier.
🔒

Chapter Quiz — Complete all 7 topics to unlock

0/7 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.