← 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.
Syntax
rust
unsafe {
    // operations the compiler cannot verify
}

unsafe fn name() {}

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
// Entry point: execution of the program starts here
fn main() {
    // Declare an immutable variable `value`, set to `10`
    let value = 10;
    // Declare an immutable variable `raw_pointer`, set to `&value as *const i32`
    let raw_pointer = &value as *const i32;
    unsafe {
        // Print "Value through raw pointer: {}" to the console, substituting in `*raw_pointer` (with a trailing newline)
        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
}

// Entry point: execution of the program starts here
fn main() {
    // Declare an immutable variable `result`, set to `unsafe { dangerous_operation() }`
    let result = unsafe { dangerous_operation() };
    // Print "Result: {}" to the console, substituting in `result` (with a trailing newline)
    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
// Entry point: execution of the program starts here
fn main() {
    // Declare a mutable variable `value`, initialized to `5`
    let mut value = 5;
    {
        // Declare an immutable variable `pointer`, set to `&mut value as *mut i32`
        let pointer = &mut value as *mut i32;
        unsafe {
            *pointer += 10;
        }
    }
    // Print "value = {}" to the console, substituting in `value` (with a trailing newline)
    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
// Define the function `safe_get` taking `arr`, `index` and returning a `Option<i32>`
fn safe_get(arr: &[i32], index: usize) -> Option<i32> {
    // Check whether `index < arr.len()`
    if index < arr.len() {
        Some(unsafe { *arr.get_unchecked(index) })
    // Otherwise, run this branch
    } else {
        None
    }
}

// Entry point: execution of the program starts here
fn main() {
    // Declare an immutable variable `data`, set to `[1, 2, 3]`
    let data = [1, 2, 3];
    // Print "{:?}" to the console, substituting in `safe_get(&data, 1)` (with a trailing newline)
    println!("{:?}", safe_get(&data, 1));
    // Print "{:?}" to the console, substituting in `safe_get(&data, 10)` (with a trailing newline)
    println!("{:?}", safe_get(&data, 10));
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
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.