When you need raw pointers
Most Rust code never sees a raw pointer. References (`&T`, `&mut T`) and smart pointers (`Box`, `Rc`, `Arc`) cover almost all needs. Reach for `*const T` and `*mut T` only when you cross an FFI boundary, build an intrusive linked-list-style data structure where multiple references must coexist, or implement a lock-free algorithm with atomics. Every dereference of a raw pointer is `unsafe`.
null, NonNull, Unique
`ptr::null` and `ptr::null_mut` produce typed null pointers. `NonNull<T>` is a `*const T` guaranteed not to be null — usable as a niche optimisation (Option<NonNull<T>> is the same size as a raw pointer). `Unique<T>` is unstable but sketches a single-owner raw pointer used internally by Box and Vec.
For deeper background, see a complete cheat-sheet of std collection complexities for the broader context behind this section.
read, write, copy
`ptr::read` and `ptr::write` move a value through a raw pointer without dropping or initialising the destination. `ptr::copy` and `ptr::copy_nonoverlapping` are wrappers around memcpy / memmove. All require correctly aligned, non-null pointers and proper lifetime management — they are unsafe and easy to misuse.
Pointer arithmetic
`add`, `sub`, `offset` move a pointer by a multiple of `mem::size_of::<T>()`. They are unsafe — out-of-bounds arithmetic is undefined behaviour even without dereferencing. Use `wrapping_add` / `wrapping_offset` for the rare case where wrap-around is intentional.
For deeper background, see the official Rust API guidelines for module-level design for the broader context behind this section.
Volatile and atomic
`ptr::read_volatile` and `ptr::write_volatile` opt out of compiler optimisations that would coalesce or reorder accesses — required for memory-mapped I/O. For shared-memory concurrency between threads, use the `AtomicUsize` etc. types in `std::sync::atomic` rather than raw pointers and volatile.