ExamplesBy LevelBy TopicLearning Paths
704 Fundamental

dereferencing raw ptr

Functional Programming

Tutorial Video

Text description (accessibility)

This video demonstrates the "dereferencing raw ptr" functional Rust example. Difficulty level: Fundamental. Key concepts covered: Functional Programming. This example covers a specific aspect of Rust's unsafe programming model: raw memory manipulation, FFI interop, allocator customization, or soundness principles. Key difference from OCaml: 1. **Safety model**: Rust requires explicit `unsafe` for these operations; OCaml achieves safety through the GC and type system without explicit unsafe regions.

Tutorial

The Problem

This example covers a specific aspect of Rust's unsafe programming model: raw memory manipulation, FFI interop, allocator customization, or soundness principles. These topics are essential for systems programming — writing OS components, device drivers, game engines, and any code that must interact with C libraries or control memory layout precisely. Rust's unsafe system is designed to confine unsafety to small, auditable regions while maintaining safety in the surrounding code.

🎯 Learning Outcomes

  • • The specific unsafe feature demonstrated: dereferencing raw ptr
  • • When this feature is necessary vs when safe alternatives exist
  • • How to use it correctly with appropriate SAFETY documentation
  • • The invariants that must be maintained for the operation to be sound
  • • Real-world contexts: embedded systems, OS kernels, C FFI, performance-critical code
  • Code Example

    #![allow(clippy::all)]
    //! # Dereferencing Raw Pointers
    
    pub fn raw_ptr_example() -> i32 {
        let x = 42;
        let ptr = &x as *const i32;
        unsafe { *ptr }
    }
    
    pub fn mutable_raw_ptr() -> i32 {
        let mut x = 10;
        let ptr = &mut x as *mut i32;
        unsafe {
            *ptr += 5;
            *ptr
        }
    }
    
    pub fn ptr_from_address(addr: usize) -> *const i32 {
        addr as *const i32
    }
    
    pub fn null_check(ptr: *const i32) -> Option<i32> {
        if ptr.is_null() {
            None
        } else {
            unsafe { Some(*ptr) }
        }
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
        #[test]
        fn test_raw() {
            assert_eq!(raw_ptr_example(), 42);
        }
        #[test]
        fn test_mut() {
            assert_eq!(mutable_raw_ptr(), 15);
        }
        #[test]
        fn test_null() {
            let x = 5;
            assert_eq!(null_check(&x), Some(5));
            assert_eq!(null_check(std::ptr::null()), None);
        }
    }

    Key Differences

  • Safety model: Rust requires explicit unsafe for these operations; OCaml achieves safety through the GC and type system without explicit unsafe regions.
  • FFI approach: Rust uses raw C types directly with extern "C"; OCaml uses ctypes which wraps C types in OCaml values.
  • Memory control: Rust allows complete control over memory layout (#[repr(C)], custom allocators); OCaml's GC manages memory layout automatically.
  • Auditability: Rust unsafe regions are syntactically visible and toolable; OCaml unsafe operations (Obj.magic, direct C calls) are also explicit but less common.
  • OCaml Approach

    OCaml's GC and type system eliminate most of the need for these unsafe operations. The equivalent functionality typically uses:

  • • C FFI via the ctypes library for external function calls
  • Bigarray for controlled raw memory access
  • • The GC for memory management (no manual allocators needed)
  • Bytes.t for mutable byte sequences
  • OCaml programs rarely need operations equivalent to these Rust unsafe patterns.

    Full Source

    #![allow(clippy::all)]
    //! # Dereferencing Raw Pointers
    
    pub fn raw_ptr_example() -> i32 {
        let x = 42;
        let ptr = &x as *const i32;
        unsafe { *ptr }
    }
    
    pub fn mutable_raw_ptr() -> i32 {
        let mut x = 10;
        let ptr = &mut x as *mut i32;
        unsafe {
            *ptr += 5;
            *ptr
        }
    }
    
    pub fn ptr_from_address(addr: usize) -> *const i32 {
        addr as *const i32
    }
    
    pub fn null_check(ptr: *const i32) -> Option<i32> {
        if ptr.is_null() {
            None
        } else {
            unsafe { Some(*ptr) }
        }
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
        #[test]
        fn test_raw() {
            assert_eq!(raw_ptr_example(), 42);
        }
        #[test]
        fn test_mut() {
            assert_eq!(mutable_raw_ptr(), 15);
        }
        #[test]
        fn test_null() {
            let x = 5;
            assert_eq!(null_check(&x), Some(5));
            assert_eq!(null_check(std::ptr::null()), None);
        }
    }
    ✓ Tests Rust test suite
    #[cfg(test)]
    mod tests {
        use super::*;
        #[test]
        fn test_raw() {
            assert_eq!(raw_ptr_example(), 42);
        }
        #[test]
        fn test_mut() {
            assert_eq!(mutable_raw_ptr(), 15);
        }
        #[test]
        fn test_null() {
            let x = 5;
            assert_eq!(null_check(&x), Some(5));
            assert_eq!(null_check(std::ptr::null()), None);
        }
    }

    Deep Comparison

    Dereferencing Raw Pointers

    See example files for OCaml vs Rust comparison.

    Exercises

  • Minimize unsafe: Find the smallest possible unsafe region in the source and verify that all safe code is outside the unsafe block.
  • Safe alternative: Identify if a safe alternative exists for the demonstrated technique (e.g., bytemuck for transmute, CString for FFI strings) and implement it.
  • SAFETY documentation: Write a complete SAFETY comment for each unsafe block listing preconditions, invariants, and what would break if violated.
  • Open Source Repos