---
title: unsafe Blocks and the Soundness Contract
url: https://doc.liz6.com/en/rust/07-unsafe-rust/02-unsafe-blocks-and-the-soundness-contract
locale: en
area: rust
tags:
- rust
- unsafe-rust
date: 2026-06-30
modified: 2026-07-16
description: The `unsafe` keyword does not "disable" safety checks; rather, it transfers the safety contract that the compiler normally guarantees to the programmer. You can dereference raw pointers, call unsafe functions, and access mutable static variables, but you must ensure that no matter what the caller does, it will not trigger UB (Undefined Behavior). The real difficulty in writing unsafe code is not "being able to write it," but rather wrapping it in a safe abstraction.
---

# unsafe Blocks and the Soundness Contract

> The `unsafe` keyword does not "disable" safety checks; rather, it transfers the safety contract that the compiler normally guarantees to the programmer. You can dereference raw pointers, call unsafe functions, and access mutable static variables, but you must ensure that no matter what the caller does, it will not trigger UB (Undefined Behavior). The real difficulty in writing unsafe code is not "being able to write it," but rather wrapping it in a safe abstraction.

## unsafe is Not "Disabling All Safety Checks"

An `unsafe {}` block enables only 5 capabilities; all other Rust safety checks remain in effect:

1. Dereferencing raw pointers (`*const T`, `*mut T`)
2. Calling `unsafe fn`
3. Accessing or modifying `static mut` variables
4. Implementing `unsafe trait`s (e.g., `Send`, `Sync`, `GlobalAlloc`)
5. Accessing fields of a `union`

**The borrow checker remains active within unsafe blocks** — you cannot create two `&mut T` references pointing to the same data. This is UB, and the compiler will still reject it even within unsafe code.

The key to understanding unsafe: it is not "I take on all safety responsibilities," but rather "I take on the responsibility for the part that the compiler cannot automatically verify."

## Soundness: The Boundary of Safety

The signature of an `unsafe fn` defines a **contract**. The caller must satisfy the preconditions, otherwise UB may occur. This pattern is ubiquitous in the standard library:

```rust
// Safe externally, unsafe internally — the caller only needs to pass correct arguments, no need to write unsafe
pub fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) {
    assert!(mid <= self.len());      // Runtime check — this is the safety boundary!
    let ptr = self.as_mut_ptr();
    unsafe { (from_raw_parts_mut(ptr, mid),
              from_raw_parts_mut(ptr.add(mid), self.len() - mid)) }
}
```

If the assertion passes, the subsequent unsafe operations are sound — `mid <= len` guarantees that the two slices do not overlap. If the assertion fails, it panics directly — it does not enter the unsafe region. This is the most common pattern in the standard library: **safe code validates conditions, and unsafe code executes under the premise that those conditions hold**.

Crucially, the soundness of unsafe code depends not only on the logic within the unsafe block but also on the **invariants maintained by external safe code**. If the unsafe code you write assumes certain invariants of a data structure (such as `len <= cap`), then all external safe code that modifies that data structure must maintain that invariant.

## UB (Undefined Behavior): The List to Avoid

- Dereferencing null, dangling, or misaligned pointers
- Violating borrowing rules (even within unsafe code)
- Data races
- Calling functions with the wrong ABI
- Creating invalid values: `bool` that is neither 0 nor 1, null references, `char` that is a surrogate
- Unwinding across FFI boundaries (panicking within an `extern "C"` function)

## Miri: Automated UB Detection

Miri interprets Rust at the MIR level, tracking provenance and borrow states:

```bash
cargo +nightly miri test
```

It can detect use-after-free, out-of-bounds access, data races, and alignment violations. Any crate containing `unsafe` should be run with Miri — while it cannot prove soundness, it can catch obvious UB.

## References

- **Rustonomicon**: "What Unsafe Can Do", Soundness requirements
- **Rust Reference**: Behavior considered undefined
- **Miri**: github.com/rust-lang/miri

*Keywords: unsafe, soundness, UB, Miri, safety contract, invariant, raw pointer*
