๐Ÿฆ€ Functional Rust
๐ŸŽฌ Rust Ownership in 30 seconds Visual walkthrough of ownership, moves, and automatic memory management.
๐Ÿ“ Text version (for readers / accessibility)

โ€ข Each value in Rust has exactly one owner โ€” when the owner goes out of scope, the value is dropped

โ€ข Assignment moves ownership by default; the original binding becomes invalid

โ€ข Borrowing (&T / &mut T) lets you reference data without taking ownership

โ€ข The compiler enforces: many shared references OR one mutable reference, never both

โ€ข No garbage collector needed โ€” memory is freed deterministically at scope exit

704: Dereferencing Raw Pointers Safely

Difficulty: 4 Level: Expert Safely read through a `const T` / `mut T` by proving four invariants before entering `unsafe`.

The Problem This Solves

Raw pointers โ€” `const T` and `mut T` โ€” are Rust's escape hatch for C interop, custom allocators, and intrusive data structures. Unlike references, they carry no lifetime and no borrow-checker protection. The compiler trusts you to know what you're doing. When you get it wrong, you get undefined behaviour: memory corruption, stack smashing, data races. The challenge is that dereferencing a raw pointer is safe in intent far more often than the type system can prove. You receive a pointer from a C library; you know it's valid. You have a `*mut Node` in a linked list you built yourself; you know no other reference exists. The compiler can't see any of that โ€” you have to write the proof yourself. The canonical pattern wraps the dereference in a function that explicitly checks: (1) non-null, (2) properly aligned, (3) points to an initialized value, and (4) no exclusive `&mut` reference to the same location exists. Document these proofs in `// SAFETY:` comments โ€” they are the contract future maintainers will rely on.

The Intuition

Think of a raw pointer as a street address written on a piece of paper. It might be valid. It might be a demolished building. It might be in a country that uses different door-numbering rules. Before you walk through the door, you need to verify the address is real (`is_null()`), that your key fits the lock (alignment), that something actually lives there (initialized), and that nobody else has a key right now (no `&mut` alias). The `safe_deref<T: Copy>` pattern returns `Option<T>` โ€” `None` on any check failure, `Some(value)` on success. The `Copy` bound lets you take a snapshot of the value without worrying about aliasing after the fact.

How It Works in Rust

pub fn safe_deref<T: Copy>(ptr: *const T) -> Option<T> {
 // Check 1: non-null
 if ptr.is_null() { return None; }
 
 // Check 2: alignment
 if (ptr as usize) % std::mem::align_of::<T>() != 0 {
     return None;
 }
 
 Some(unsafe {
     // SAFETY:
     // 1. ptr is non-null (checked above).
     // 2. ptr is properly aligned (checked above).
     // 3. Caller guarantees pointee is initialized.
     // 4. T: Copy โ€” shared read, no exclusive alias needed.
     *ptr
 })
}
For mutable writes, the same checks apply plus you must prove exclusive access:
pub fn safe_write<T>(ptr: *mut T, val: T) -> bool {
 if ptr.is_null() { return false; }
 if (ptr as usize) % std::mem::align_of::<T>() != 0 { return false; }
 unsafe {
     // SAFETY: non-null, aligned, and caller guarantees no other reference.
     ptr.write(val);
 }
 true
}
Use `NonNull<T>` (example 705) when you want to bake the non-null invariant into the type itself.

What This Unlocks

Key Differences

ConceptOCamlRust
DereferenceTransparent, GC-managed`*ptr` inside `unsafe {}`
Null checkNot needed (no null)`.is_null()` or `NonNull<T>`
AlignmentGuaranteed by runtimeMust be verified manually
Dangling riskGC prevents itCaller must track lifetimes
Safety documentationNone needed`// SAFETY:` comment is the proof
Exclusive accessGC tracks via value semanticsCaller proves no `&mut` alias
//! 704 โ€” Dereferencing Raw Pointers Safely
//! Four pre-conditions: non-null, aligned, initialized, no exclusive alias.

use std::mem;

/// Safely dereference a *const T, checking null and alignment.
/// Returns None if either check fails.
pub fn safe_deref<T: Copy>(ptr: *const T) -> Option<T> {
    if ptr.is_null() {
        return None;
    }
    if (ptr as usize) % mem::align_of::<T>() != 0 {
        return None;
    }
    Some(unsafe {
        // SAFETY:
        // 1. ptr is non-null (checked above).
        // 2. ptr is properly aligned (checked above).
        // 3. Caller is responsible for ensuring pointee is initialized.
        // 4. T: Copy means we take a shared read; no exclusive alias needed.
        *ptr
    })
}

/// Safely read through a *mut T with the same checks.
pub fn safe_deref_mut<T: Copy>(ptr: *mut T) -> Option<T> {
    safe_deref(ptr as *const T)
}

/// Write through a *mut T after null+alignment checks.
pub fn safe_write<T>(ptr: *mut T, value: T) -> bool {
    if ptr.is_null() { return false; }
    if (ptr as usize) % mem::align_of::<T>() != 0 { return false; }
    unsafe {
        // SAFETY: non-null + aligned checked above; caller ensures no alias.
        *ptr = value;
    }
    true
}

fn main() {
    // Valid pointer
    let x: u64 = 0xDEAD_BEEF;
    println!("valid: {:?}", safe_deref(&x as *const u64));

    // Null pointer
    let null: *const u64 = std::ptr::null();
    println!("null: {:?}", safe_deref(null));

    // Misaligned pointer check
    let bytes = [0u8; 16];
    let misaligned = bytes[1..].as_ptr() as *const u64;
    println!("misaligned (likely None): {:?}", safe_deref(misaligned));

    // Write via raw pointer
    let mut val: i32 = 0;
    let ok = safe_write(&mut val as *mut i32, 777);
    println!("write ok={ok}, val={val}");

    // Array traversal via raw pointer
    let arr = [10i32, 20, 30, 40];
    let ptr: *const i32 = arr.as_ptr();
    for i in 0..arr.len() {
        let elem_ptr = unsafe {
            // SAFETY: i < arr.len(); ptr valid for arr.len() elements.
            ptr.add(i)
        };
        println!("arr[{i}] = {:?}", safe_deref(elem_ptr));
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_valid_deref() {
        let x = 99i32;
        assert_eq!(safe_deref(&x as *const i32), Some(99));
    }

    #[test]
    fn test_null_deref() {
        assert_eq!(safe_deref::<i32>(std::ptr::null()), None);
    }

    #[test]
    fn test_safe_write() {
        let mut v = 0i32;
        assert!(safe_write(&mut v, 42));
        assert_eq!(v, 42);
    }

    #[test]
    fn test_null_write() {
        assert!(!safe_write::<i32>(std::ptr::null_mut(), 1));
    }
}
(* OCaml: all dereferences are safe and transparent.
   We model the concept of validated access with option types. *)

type 'a validated_ptr = Valid of 'a | Null | Misaligned

let safe_deref (p : 'a validated_ptr) : 'a option =
  match p with
  | Valid v    -> Some v
  | Null       -> None
  | Misaligned -> None

let () =
  let vp = Valid 42 in
  let np = Null in
  Printf.printf "valid deref: %s\n"
    (match safe_deref vp with Some v -> string_of_int v | None -> "null");
  Printf.printf "null deref:  %s\n"
    (match safe_deref np with Some v -> string_of_int v | None -> "null")