--- id: ptr-02 original_id: P.UNS.PTR.02 level: P impact: MEDIUM --- # Prefer NonNull Over *mut T ## Summary Use `NonNull` instead of `*mut T` when the pointer should never be null. This enables null pointer optimization and makes the intent clear. ## Rationale - `NonNull` guarantees non-null at the type level - Enables niche optimization: `Option>` is the same size as `*mut T` - Makes invariants explicit in the type system - Covariant over `T` (like `&T`), which is usually what you want ## Bad Example ```rust // DON'T: Use *mut when pointer is always non-null struct MyBox { ptr: *mut T, // Invariant: never null, but not enforced } impl MyBox { pub fn new(value: T) -> Self { let ptr = Box::into_raw(Box::new(value)); // ptr is guaranteed non-null, but type doesn't show it Self { ptr } } pub fn get(&self) -> &T { // Must add null check or document the invariant unsafe { &*self.ptr } } } ``` ## Good Example ```rust use std::ptr::NonNull; // DO: Use NonNull when pointer is never null struct MyBox { ptr: NonNull, // Type guarantees non-null } impl MyBox { pub fn new(value: T) -> Self { let ptr = Box::into_raw(Box::new(value)); // SAFETY: Box::into_raw never returns null let ptr = unsafe { NonNull::new_unchecked(ptr) }; Self { ptr } } pub fn get(&self) -> &T { // SAFETY: NonNull guarantees ptr is valid unsafe { self.ptr.as_ref() } } } impl Drop for MyBox { fn drop(&mut self) { // SAFETY: ptr was created from Box::into_raw unsafe { drop(Box::from_raw(self.ptr.as_ptr())); } } } // DO: Niche optimization with Option struct OptionalBox { ptr: Option>, // Same size as *mut T! } ``` ## NonNull API ```rust use std::ptr::NonNull; // Creating NonNull let ptr: NonNull = NonNull::new(raw_ptr).expect("null pointer"); let ptr: NonNull = unsafe { NonNull::new_unchecked(raw_ptr) }; let ptr: NonNull = NonNull::dangling(); // For ZSTs or uninitialized // Using NonNull let raw: *mut i32 = ptr.as_ptr(); let reference: &i32 = unsafe { ptr.as_ref() }; let mut_ref: &mut i32 = unsafe { ptr.as_mut() }; // Casting let ptr: NonNull = ptr.cast::(); ``` ## When to Use *mut T Instead - When null is a valid/expected value - FFI with C code that may return null - When variance matters (NonNull is covariant, sometimes you need invariance) ## Checklist - [ ] Is my pointer ever null? If no, use NonNull - [ ] Do I need null pointer optimization? - [ ] Is the variance correct for my use case? ## Related Rules - `ptr-03`: Use PhantomData for variance and ownership - `safety-06`: Don't expose raw pointers in public APIs