//! 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")