Unsafe Rust

Unsafe Blocks

An unsafe block (unsafe { ... }) is an expression that grants access to the five unsafe superpowers within its lexical scope while asserting to the compiler that the programmer has verified all safety invariants for the enclosed operations.

Unsafe Blocks Are Expressions

Like regular { ... } blocks in Rust, unsafe { ... } is an expression that evaluates to the value of its final expression. This allows keeping unsafe blocks as narrow as possible—wrapping only the single unsafe call or dereference rather than an entire multi-line algorithm:

fn parse_ascii_digit(byte: u8) -> Option<char> {
    if byte.is_ascii_digit() {
        // The `unsafe` block wraps only the single `from_u32_unchecked` expression:
        // SAFETY: All ASCII digits (`0x30..=0x39`) are valid Unicode scalar values.
        let ch = unsafe { char::from_u32_unchecked(u32::from(byte)) };
        Some(ch)
    } else {
        None
    }
}

fn main() {
    println!("b'7' -> {:?}", parse_ascii_digit(b'7'));
    println!("b'Z' -> {:?}", parse_ascii_digit(b'Z'));
}
b'7' -> Some('7')
b'Z' -> None

Why the Trust Boundary Is Larger Than the unsafe {} Braces

Although only the lines inside unsafe { ... } can perform raw pointer dereferences or call unsafe fn, safe code within the same module can invalidate the invariants that an unsafe block relies upon.

Module Privacy Is the True Safety Boundary:

+------------------------- `mod ring_buffer` -------------------------+
|  pub struct RingBuffer<T> {                                         |
|      ptr: *mut T,   // Private field!                               |
|      cap: usize,    // Private field!                               |
|      len: usize,    // Private field!                               |
|  }                                                                  |
|                                                                     |
|  Safe methods in this module (`clear`, `truncate`, `reserve`) can   |
|  modify `self.len` and `self.cap`. If a safe method sets            |
|  `self.len = 9999`, the `unsafe` block in `pop()` will read         |
|  uninitialized memory!                                              |
+---------------------------------------------------------------------+

For example, if cap or len were pub fields on a custom vector type, external safe code could write vec.len = 1000; without using unsafe, causing a subsequent vec[0] read (which uses an internal unsafe block) to read uninitialized heap memory. Therefore, fields whose invariants are relied upon by unsafe blocks must always be private to the encapsulating module.

Granular vs. Broad unsafe Blocks

Keep each unsafe block small enough that a single // SAFETY: comment above it justifies every unsafe operation inside the block:

fn copy_header_and_trailer(src: &[u8], dst: &mut [u8; 4]) {
    assert!(src.len() >= 4, "source slice must have at least 4 bytes");

    let src_ptr = src.as_ptr();
    let dst_ptr = dst.as_mut_ptr();

    // SAFETY: `src.len() >= 4` and `dst` has length 4; `src` (&[u8]) and
    // `dst` (&mut [u8; 4]) cannot overlap because `dst` is uniquely borrowed.
    unsafe {
        std::ptr::copy_nonoverlapping(src_ptr, dst_ptr, 2);
        std::ptr::copy_nonoverlapping(src_ptr.add(src.len() - 2), dst_ptr.add(2), 2);
    }
}

fn main() {
    let packet = [0xAA, 0xBB, 0x01, 0x02, 0x03, 0xCC, 0xDD];
    let mut summary = [0_u8; 4];

    copy_header_and_trailer(&packet, &mut summary);
    println!("summary: {:02x?}", summary);
}
summary: [aa, bb, cc, dd]

Unused Unsafe Warning (#[warn(unused_unsafe)])

If an unsafe { ... } block contains no operations that actually require unsafe, the compiler emits an unused_unsafe warning so redundant unsafe blocks can be removed during refactoring.