Skip to content
Unsafe Rust
step 1/4

Reading — step 1 of 4

Learn

~1 min readMacros and Unsafe

unsafe lets you do things the borrow checker can't verify. Used in:

  • Calling C libraries (FFI)
  • Implementing data structures (linked lists, raw pointers)
  • Hardware/OS interfaces
  • Performance-critical paths where you've manually verified correctness

The 5 unsafe "superpowers":

  1. Dereference a raw pointer (*ptr)
  2. Call an unsafe function
  3. Access or modify a mutable static variable
  4. Implement an unsafe trait
  5. Access fields of unions

Raw pointers:

let x = 5;
let r1: *const i32 = &x;
let r2: *mut i32 = &x as *const i32 as *mut i32;

unsafe {
    println!("{}", *r1);
    // *r2 = 10;     // would be a data race; *r2 must point to actual mutable memory
}

Raw pointers can be null, dangling, unaligned. Rust doesn't track them. They're for FFI mostly.

FFI to C:

extern "C" {
    fn abs(x: i32) -> i32;        // declared from libc
}

fn main() {
    let result = unsafe { abs(-5) };
    println!("{}", result);
}

Calling unsafe Rust functions:

unsafe fn dangerous() {
    println!("trust me");
}

fn main() {
    unsafe { dangerous(); }
}

unsafe block discipline:

  • Wrap as small a region as possible
  • Add a comment explaining WHY it's safe
  • Provide a safe wrapper API; users shouldn't need unsafe
// SAFETY: We just allocated `buf`, so `ptr` is valid for `len` bytes.
unsafe { ptr::write_bytes(ptr, 0, len); }

std::mem::transmute — bit-cast one type to another (extremely unsafe):

let bytes: [u8; 4] = unsafe { std::mem::transmute(42i32) };

Avoid unsafe when you can. Modern Rust has many sound APIs (e.g., slice::from_raw_parts, Cell::as_ptr) that wrap unsafe internally. Reach for raw unsafe only when no safe API exists.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…