Skip to content

Week 13 - Unsafe Rust: Raw Pointers, NonNull, MaybeUninit, UB

13.1 Conceptual Core

  • unsafe is not "turn off the borrow checker." It unlocks five extra capabilities:
  • Dereference raw pointers.
  • Call unsafe fns.
  • Implement unsafe traits.
  • Access static mut (now discouraged in favor of SyncUnsafeCell).
  • Access fields of unions.
  • The borrow checker, lifetime checker, type checker-all still run. unsafe widens ability, it does not weaken checks.
  • The discipline is safety contracts: every unsafe fn and every unsafe { ... } block must be paired with a comment articulating the invariants the caller is asserting. The community standard is // SAFETY: ... comments, scanned by clippy::undocumented_unsafe_blocks.

13.2 Mechanical Detail

  • *const T vs *mut T-the variance differs (*mut T is invariant in T), the legality of forming references differs (you may not produce &T from a *const T aliasing a &mut T), but otherwise they behave the same. The mut/const distinction is documentation, not enforcement.
  • NonNull<T>-a wrapper around *mut T with the niche optimization (the null bit pattern is forbidden). Use it for FFI handles and as the storage primitive in Box/Rc/Arc.
  • MaybeUninit<T>-the way to manipulate uninitialized memory legally. mem::uninitialized is deprecated for soundness reasons; MaybeUninit is the replacement. Internalize MaybeUninit::write, assume_init, assume_init_ref.
  • Provenance: a pointer carries not just an address but a provenance tag indicating which allocation it derives from. ptr::with_addr and ptr::map_addr are the safe ways to manipulate addresses without losing provenance. Read the strict-provenance proposal (std::ptr module docs).
  • The Rustonomicon's UB list: dangling references, null references, misaligned references, mutable aliasing, type confusion (transmuting padding), data races. Memorize.

13.3 Lab-"A Sound Vec"

Re-implement Vec<T> from scratch (the Nomicon's chapter 9 walk-through is the reference). Requirements: - RawVec allocator wrapper handling growth. - ZST (zero-sized type) handling-Vec<()> must work without ever allocating. - Drop correct under panic in T::drop. - Iteration via IntoIter with proper drop on partial consumption. - Pass Miri on every public method.

13.4 Idiomatic & Clippy Drill

  • clippy::undocumented_unsafe_blocks, clippy::multiple_unsafe_ops_per_block, clippy::transmute_ptr_to_ref, clippy::cast_ptr_alignment. Each maps to a documented UB class.

13.5 Production Hardening Slice

  • Run your Vec lab under cargo +nightly miri test -Zmiri-strict-provenance. Document each Miri diagnostic and the fix in a SAFETY_LOG.md. This document is the deliverable, not the code.

Comments