Appendix: Reference Implementations


title: “NonNull Reference Implementations” meta_description: “Complete reference implementations of Box, Vec, and intrusive list using NonNull.” keywords: [“nonnull implementation”, “box with nonnull”, “vec with nonnull”]

📚 Reinventing from Scratch: NonNull<T>

Chapter 11 — “Appendix: Reference Implementations”

“See it all come together.”


🎯 Complete Box Implementation

use std::alloc::{alloc, dealloc, Layout};
use std::ptr::NonNull;

pub struct MyBox<T> {
    ptr: NonNull<T>,
}

impl<T> MyBox<T> {
    pub fn new(value: T) -> Self {
        let layout = Layout::new::<T>();
        
        let ptr = unsafe {
            let p = alloc(layout) as *mut T;
            let nn = NonNull::new(p).expect("Allocation failed");
            nn.as_ptr().write(value);
            nn
        };
        
        Self { ptr }
    }
    
    pub fn into_raw(self) -> *mut T {
        let ptr = self.ptr.as_ptr();
        std::mem::forget(self);
        ptr
    }
    
    pub unsafe fn from_raw(ptr: *mut T) -> Self {
        Self {
            ptr: NonNull::new_unchecked(ptr),
        }
    }
}

impl<T> std::ops::Deref for MyBox<T> {
    type Target = T;
    
    fn deref(&self) -> &T {
        unsafe { self.ptr.as_ref() }
    }
}

impl<T> std::ops::DerefMut for MyBox<T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { self.ptr.as_mut() }
    }
}

impl<T> Drop for MyBox<T> {
    fn drop(&mut self) {
        unsafe {
            std::ptr::drop_in_place(self.ptr.as_ptr());
            let layout = Layout::new::<T>();
            dealloc(self.ptr.as_ptr() as *mut u8, layout);
        }
    }
}

📦 Complete RawVec Implementation

pub struct RawVec<T> {
    ptr: NonNull<T>,
    cap: usize,
}

impl<T> RawVec<T> {
    pub fn new() -> Self {
        Self {
            ptr: NonNull::dangling(),
            cap: 0,
        }
    }
    
    pub fn with_capacity(cap: usize) -> Self {
        if cap == 0 {
            return Self::new();
        }
        
        let layout = Layout::array::<T>(cap).unwrap();
        let ptr = unsafe {
            let p = alloc(layout) as *mut T;
            NonNull::new(p).expect("Allocation failed")
        };
        
        Self { ptr, cap }
    }
    
    pub fn grow(&mut self) {
        let new_cap = if self.cap == 0 { 4 } else { self.cap * 2 };
        
        let new_layout = Layout::array::<T>(new_cap).unwrap();
        
        let new_ptr = unsafe {
            if self.cap == 0 {
                alloc(new_layout)
            } else {
                let old_layout = Layout::array::<T>(self.cap).unwrap();
                realloc(self.ptr.as_ptr() as *mut u8, old_layout, new_layout.size())
            }
        };
        
        self.ptr = NonNull::new(new_ptr as *mut T).expect("Reallocation failed");
        self.cap = new_cap;
    }
    
    pub fn ptr(&self) -> NonNull<T> {
        self.ptr
    }
}

impl<T> Drop for RawVec<T> {
    fn drop(&mut self) {
        if self.cap != 0 {
            unsafe {
                let layout = Layout::array::<T>(self.cap).unwrap();
                dealloc(self.ptr.as_ptr() as *mut u8, layout);
            }
        }
    }
}

🎉 Series Complete!

You’ve mastered NonNull:

✅ What it guarantees (and doesn’t)
✅ The complete API
✅ Provenance and aliasing
✅ Niche optimization
✅ Fat pointers and metadata
✅ Practical patterns

Now go build amazing data structures! 🦀


Series Complete! Check out other topics:

  • Vec — Dynamic arrays
  • HashMap — Hash tables
  • Box — Smart pointers