Reinventing From Scratch — NonNull<T>
Chapter 0 — Preface: Why NonNull<T> After Box<T>?
NonNull<T> is a tiny wrapper around a raw pointer that promises one thing:
This pointer is never null.
That single promise unlocks niche optimizations (Option<NonNull<T>> has the size of a single pointer), clarifies invariants inside data structures, and reduces foot-guns compared to *mut T / *const T.
We’ll write book‑style chapters that turn NonNull<T> from a “mysterious standard type” into a tool you can confidently use to implement containers (Vec, slabs, arenas, intrusive lists) and smart pointers.
Deep Dive: Provenance, Niche Layouts, and Safe Projections
A. Invariants for NonNull<T>
- N1 (Never-Null): The wrapped pointer cannot be null from construction to drop.
- N2 (No Deref Promise):
NonNull<T>alone does not promise that a validTis there—just that the pointer is non-null. - N3 (Provenance Respect): Converting to
&T/&mut Tis only legal if you hold the corresponding borrow capability;as_ref/as_mutencode that at the type level. - N4 (Layout Discipline): Casting
NonNull<U>toNonNull<T>requiresTandUbe layout-compatible; misalignment is UB on use.
B. Option Niche Demonstration
On most platforms:
use std::ptr::NonNull;
assert_eq!(std::mem::size_of::<Option<NonNull<u8>>>(),
std::mem::size_of::<*mut u8>>());
The null pointer pattern acts as the None tag “for free,” yielding compact lists and intrusive structures.
C. Fat Pointers and Metadata
NonNull<[T]>is a fat pointer; length travels with the pointer and must be consistent with your actual allocation.NonNull<dyn Trait>pairs data with a vtable; you must not fabricate vtables—leave trait object creation to the compiler.
D. Safe Projections with Late Borrowing
Prefer to keep data as raw pointers during shuffles (memmoves, swaps), and produce &T/&mut T only at the last moment. This reduces the chance of aliasing violations and keeps the borrow window short.
E. Exercises
- Show that a zero-capacity vector may store
NonNull::dangling()and still be safe so long as no elements are read/written. - Build a singly-linked list node with
Option<NonNull<Node>>and writepush_front,pop_frontadjusting only raw pointers, turning them into refs only when reading/writingvalue.