Mojo Pointers
A pointer stores the memory address of another value rather than the value itself. Pointers let you work with memory directly — allocating, reading, writing, and freeing blocks of data. Mojo provides UnsafePointer for low-level control and is developing safe pointer types for everyday use.
The Address Analogy
Memory is like a street of numbered houses. Each house holds one value. House 1000: [ 42 ] House 1001: [ 99 ] House 1002: [ 7 ] A pointer is a piece of paper that says "go to house 1000." The pointer itself lives at some other address. ptr → 1000 → [ 42 ] Following the pointer to read the value = "dereferencing."
UnsafePointer Basics
UnsafePointer is Mojo's raw pointer type. It gives you direct memory access with no safety guarantees — you are responsible for correct use. Use it only when performance demands it or when interfacing with C libraries.
from memory import UnsafePointer
fn main():
# Allocate memory for one Int
var ptr = UnsafePointer[Int].alloc(1)
# Write a value to the allocated memory
ptr.init_pointee_copy(42)
# Read the value back
print(ptr[0]) # 42
# Free the memory when done
ptr.destroy_pointee()
ptr.free()
Pointer Lifecycle
1. alloc(n) → reserve n slots of memory 2. init_pointee_*() → write initial value(s) into the memory 3. ptr[i] → read value at index i 4. ptr[i] = v → write value at index i 5. destroy_pointee() → run destructor on stored value 6. free() → release the memory back to the system Skip step 5 or 6 → memory leak Use memory after step 6 → undefined behavior (crash or corrupt data)
Allocating Multiple Elements
from memory import UnsafePointer
fn main():
let n = 5
var arr = UnsafePointer[Float64].alloc(n)
# Initialize all elements
for i in range(n):
arr.init_pointee_copy(Float64(i) * 1.5)
# Read all elements
for i in range(n):
print(arr[i], end=" ") # 0.0 1.5 3.0 4.5 6.0
print("")
# Clean up
for i in range(n):
(arr + i).destroy_pointee()
arr.free()
Memory layout for 5 Float64 values:
arr → [ 0.0 | 1.5 | 3.0 | 4.5 | 6.0 ]
↑ ↑ ↑ ↑ ↑
arr+0 arr+1 arr+2 arr+3 arr+4
Pointer Arithmetic
Adding an integer to a pointer advances it by that many elements (not bytes). This is how you navigate through a block of memory.
from memory import UnsafePointer
fn main():
var data = UnsafePointer[Int].alloc(3)
data.init_pointee_copy(10)
(data + 1).init_pointee_copy(20)
(data + 2).init_pointee_copy(30)
print(data[0]) # 10
print((data + 1)[0]) # 20
print((data + 2)[0]) # 30
for i in range(3):
(data + i).destroy_pointee()
data.free()
Pointer arithmetic: data → address 1000 → value 10 data+1 → address 1008 → value 20 (8 bytes for Int64) data+2 → address 1016 → value 30
Null Pointer Check
An uninitialized or failed allocation produces a null pointer. Dereferencing null causes a crash. Always check before using a pointer from an external source.
from memory import UnsafePointer
fn main():
var ptr = UnsafePointer[Int]() # null pointer
if ptr:
print("Valid pointer, safe to use")
else:
print("Null pointer — do not dereference")
Passing Pointers to Functions
from memory import UnsafePointer
fn fill(ptr: UnsafePointer[Int], count: Int, value: Int):
for i in range(count):
ptr[i] = value
fn sum_array(ptr: UnsafePointer[Int], count: Int) -> Int:
var total = 0
for i in range(count):
total += ptr[i]
return total
fn main():
var buf = UnsafePointer[Int].alloc(4)
for i in range(4):
buf.init_pointee_copy(0)
fill(buf, 4, 7)
print(sum_array(buf, 4)) # 28
for i in range(4):
(buf + i).destroy_pointee()
buf.free()
When to Use Pointers
Situation | Use Pointer? ---------------------------------------|------------- Interfacing with a C library | Yes Writing a custom memory allocator | Yes Building a high-performance data struct | Yes General application logic | No — use List, String, structs Anything where safety matters most | No — use safe types
Key Takeaways
A pointer holds a memory address. UnsafePointer gives raw memory control with no automatic safety. Always follow the full lifecycle: allocate, initialize, use, destroy, free. Pointer arithmetic moves in units of the element size. Check for null before dereferencing external pointers. Reserve pointers for system-level work and performance-critical data structures — safe Mojo types cover the vast majority of everyday programming needs.
