Pointers in Mojo – Explained with Visual Analogies

pointer in mojo

In this article, you will learn pointers in Mojo, their types, life-cycle, referencing, de-referencing etc., with real-world examples, visuals and easy code snippets for beginners.

Real-World Analogy of Pointers

Imagine you have a library with many books. Each book has a unique location on a shelf, but the library doesn’t always carry the book to you. Instead, they give you the shelf number where the book is located. This shelf number is like a pointer—rather than carrying the whole book (data), you are given the location (memory address) to access it when needed. Just like in the following scenario, each pointer is pointing to a different shelf on the rack.

pointers in mojo

In programming, pointers work similarly. Instead of copying large data like a book, a pointer stores the memory location where the data is. This allows efficient access to the data, just like how the shelf number lets you access a book without moving it around.

What are pointers?

Pointers are variables that points to another variable by storing their memory address. It allows indirect access to its value. Instead of holding data directly, they point to the location in memory where the data is stored.

pointers in memory

Syntax

let myPointer: UnsafePointer<Int32>

Example Code

  • Imports UnsafePointer: Brings in UnsafePointer from the memory module to use in the code.
  • Declare a pointer: myPointer is an UnsafePointer of type Int32, and it allocates memory for one integer.
  • Initialise value: Sets the value at the allocated memory to 42.
  • Print value: Outputs the value stored at the memory location using myPointer[].val.
  • Clean up: Destroys the value and then frees the allocated memory to prevent memory leaks.
var myPointer: UnsafePointer[Int32] = UnsafePointer[Int32].alloc(1)
myPointer.init_pointee(42)
print(myPointer[].val)

myPointer.destroy_pointee()
myPointer.free()

Output

42

Life Cycle of a Pointer in Mojo

The life cycle of pointer shows that a pointer in programming could be in any one of these 5  states which are as follows:

life-cycle of a pointer in mojo

Uninitialized

A pointer is declared but not yet assigned any memory address or value.

Syntax

var ptr: UnsafePointer[Int]

Example Code

  • var value: Int = 42: Declares an integer variable value and initializes it with 42.var ptr: UnsafePointer[Int] = UnsafePointer[Int].
  • address_of(value): Initializes ptr to hold the address of value.
  • The address_of method assigns the memory address of value to ptr.print("Address stored in ptr: \(ptr)"): Prints the memory address that ptr holds. This shows the address where value is stored.
  • print("Value pointed to by ptr: \(ptr[])"): Dereferences ptr to access the value it points to and prints 42.
var value: Int = 42  // Declare an integer variable
var ptr: UnsafePointer[Int] = UnsafePointer[Int].address_of(value)  // Initialize the pointer to point to the address of 'value'

// Print the address stored in the pointer and the value it points to
print("Address stored in ptr: \(ptr)")
print("Value pointed to by ptr: \(ptr[])")

Output

Address stored in ptr: <memory address>
Value pointed to by ptr: 42

Null

A null pointer has an address of 0, which means it doesn’t point to any valid memory location.

Syntax

ptr = UnsafePointer[Int]() // Points to address 0

Example Code

  • var ptr: UnsafePointer[Int] = UnsafePointer[Int](): Initializes ptr as a null pointer, which means it points to address 0 and does not reference any valid memory.
  • if ptr == UnsafePointer[Int]() checks if ptr is a null pointer.
  • print("ptr is a null pointer and does not point to any valid address."): Outputs a message stating that ptr is a null pointer.
  • If ptr were not null, the else block would print the memory address and the value it points to. However, since it is null, this part will not run.
var ptr: UnsafePointer[Int] = UnsafePointer[Int]()  // Initializes ptr to a null pointer, pointing to address 0

// Check if the pointer is null and print an appropriate message
if ptr == UnsafePointer[Int]() {
    print("ptr is a null pointer and does not point to any valid address.")
} else {
    print("Address stored in ptr: \(ptr)")
    print("Value pointed to by ptr: \(ptr[])")
}

Output

ptr is a null pointer and does not point to any valid address.
real world example of states of pointer

Pointing to Allocated, Uninitialized Memory

The alloc() method allocates a block of memory but doesn’t initialize it

Syntax

ptr = UnsafePointer[Int].alloc(1) // Allocates space for one Int

Example Code

  • var ptr: UnsafePointer[Int] = UnsafePointer[Int].alloc(1): Allocates memory for one Int and assigns the address to ptr.
  • ptr.init_pointee(42): Initializes the allocated memory with the value 42.
  • print("Address stored in ptr: \(ptr)"): Outputs the memory address stored in ptr.
  • print("Value at allocated memory: \(ptr[])"): Dereferences ptr to access and print the value 42.
  • ptr.destroy_pointee(): Cleans up the memory by destroying the value stored at ptr.
  • ptr.free(): Frees the allocated memory to prevent memory leaks.
var ptr: UnsafePointer[Int] = UnsafePointer[Int].alloc(1)  // Allocates space for one Int

// Initialize the allocated memory with a value
ptr.init_pointee(42)

// Print the address stored in the pointer
print("Address stored in ptr: \(ptr)")

// Print the value at the allocated memory
print("Value at allocated memory: \(ptr[])")

// Clean up the allocated memory
ptr.destroy_pointee()
ptr.free()

Output

Address stored in ptr: <memory_address>
Value at allocated memory: 42

Pointing to Initialize Memory

After allocating memory, you can initialize it by copying or moving a value, or by pointing to an existing value.

Syntax

ptr.init_pointee_copy(value)       // Copies value into memory
ptr.init_pointee_move(value^)      // Moves value into memory
ptr = UnsafePointer[Int].address_of(value)  // Points to existing value

Once initialized, you can read or modify the value

oldValue = ptr[]                  // Reads the value
ptr[] = newValue                  // Modifies the value

Example Code

  • var value: Int = 50: Declares and initializes value with 50.
  • var ptr: UnsafePointer[Int] = UnsafePointer[Int].alloc(1): Allocates memory for one Int and assigns the address to ptr.
  • ptr.init_pointee_copy(value): Copies value (which is 50) into the allocated memory.
  • var valueToMove: Int = 75: Declares another integer valueToMove initialized with 75.
    ptr.init_pointee_move(valueToMove^): Moves valueToMove into the allocated memory using a “move” operation.
  • ptr = UnsafePointer[Int].address_of(value): Points ptr to the address of value.
var value: Int = 50
var ptr: UnsafePointer[Int] = UnsafePointer[Int].alloc(1)  // Allocates space for one Int

// Copying the value into allocated memory
ptr.init_pointee_copy(value)
print("Value after init_pointee_copy: \(ptr[])")

// Moving the value into allocated memory (requires a copy of the original value)
var valueToMove: Int = 75
ptr.init_pointee_move(valueToMove^)
print("Value after init_pointee_move: \(ptr[])")

// Pointing to an existing variable's address
ptr = UnsafePointer[Int].address_of(value)
print("Value at address_of(value): \(ptr[])")

Output

Value after init_pointee_copy: 50
Value after init_pointee_move: 75
Value at address_of(value): 50

Dangling

After freeing the allocated memory using free(), the pointer becomes dangling. It still holds the old address, but the memory is no longer valid

Syntax

ptr.free() // Frees the allocated memory

Example Code

  • var ptr: UnsafePointer[Int] = UnsafePointer[Int].alloc(1): Allocates memory for one Int and assigns the address to ptr.
  • ptr.init_pointee(100): Initializes the allocated memory with the value 100.
  • ptr.free(): Frees the allocated memory, making it available for other uses in the program.
  • print("Memory has been freed."): Outputs a message indicating that the memory has been freed.
var ptr: UnsafePointer[Int] = UnsafePointer[Int].alloc(1)  // Allocates space for one Int

ptr.init_pointee(100)  // Initializes the memory with the value 100
print("Value before freeing: \(ptr[])")

ptr.free()  // Frees the allocated memory
print("Memory has been freed.")

Output

Value before freeing: 100
Memory has been freed.

Types of Pointers in Mojo

There are two main types of pointers that mojo offers, they are as follows

  • Unsafe Pointers 
  • Safe Pointers

Unsafe Pointers

Unsafe pointers in Mojo are used for low-level memory management. They allow you to manually work with memory addresses and perform operations directly on them. This gives you more control over memory, but also requires careful handling to avoid errors.

unsafe pointers in mojo

Syntax

var pointer: UnsafePointer[Type]

Example Code

  • var value: Int = 10: Declares an integer variable value and initializes it with 10.
  • var ptr: UnsafePointer[Int] = UnsafePointer[Int].address_of(value): Declares a pointer ptr and stores the memory address of value in it using address_of.
  • print(ptr[]): Dereferences ptr to access the value at the memory address it points to and prints the value, which is 10.
var value: Int = 10
var ptr: UnsafePointer[Int] = UnsafePointer[Int].address_of(value)

print(ptr[])

Output

10

Referencing with Unsafe Pointers

Referencing involves obtaining the memory address of a variable. When you reference a variable in Mojo, you create a pointer that holds this address, allowing you to directly access or modify the variable’s value through the pointer.

Syntax

var number: Int = 10
var ptr: UnsafePointer[Int] = UnsafePointer[Int].address_of(number)

Example Code

  • var value: Int = 42: Declares an Int variable named value and initializes it with 42.
  • var ptr: UnsafePointer[Int] = UnsafePointer[Int].address_of(value): Creates an UnsafePointer named ptr that holds the address of value.
  • ptr[] = 55: Dereferences the pointer ptr to update the value of value to 55.
  • print("Updated value: \(value)"): Prints the updated value of value.
var value: Int = 42
var ptr: UnsafePointer[Int] = UnsafePointer[Int].address_of(value)

ptr[] = 55  // Updating the value using the pointer

print("Updated value: \(value)")  // Print the updated value of 'value'

Output

Updated value: 55

Dereferencing with Unsafe Pointer

Dereferencing means using a pointer to access the value stored at the memory address it points to. This is how you read or modify the data associated with that address.

Syntax

In Mojo, dereferencing is done using the [] syntax:

var value = ptr[]           // Reads the value stored at the memory address
ptr[] = 20                  // Modifies the value at the memory address

Example Code

  • var value: Int = 100: Declares an Int variable named value and initializes it with 100.
  • var ptr: UnsafePointer[Int] = UnsafePointer[Int].address_of(value): Creates an UnsafePointer named ptr that holds the address of value.
  • var dereferencedValue: Int = ptr[]: Dereferences the pointer ptr to read and store the value of value, which is 100.
  • print("Original value: \(dereferencedValue)"): Prints the original value of value, which is 100.
  • ptr[] = 200: Uses the pointer ptr to update the value of value to 200.
  • print("Updated value: \(value)"): Prints the updated value of value, which is 200.
var value: Int = 100
var ptr: UnsafePointer[Int] = UnsafePointer[Int].address_of(value)

var dereferencedValue: Int = ptr[]  // Dereferencing to get the value
print("Original value: \(dereferencedValue)")

ptr[] = 200  // Updating the value using the pointer
print("Updated value: \(value)")

Output

Original value: 100
Updated value: 200

Declaration with Unsafe Pointers

Declaring a pointer defines a variable that can hold the address of another variable or allocated memory. At this stage, the pointer does not yet point to any specific memory address, making it uninitialized.

Syntax

var ptr: UnsafePointer[Type]

Example Code

  • var value: Int = 50: Declares an Int variable value and assigns it the number 50.
  • var numberPointer: UnsafePointer[Int] = UnsafePointer[Int].address_of(value): Initializes numberPointer to point to the memory address of value.
  • print(numberPointer[]): Dereferences numberPointer to access and print the value it points to, which is 50.
var value: Int = 50
var numberPointer: UnsafePointer[Int] = UnsafePointer[Int].address_of(value)

print(numberPointer[])

Output

50

Initialization with Unsafe Pointer

Initialising a pointer assigns it a valid memory address. This can be done by either allocating memory or referencing the address of an existing variable.

Syntax

var pointerName: UnsafePointer[Type] = UnsafePointer[Type].address_of(variable)

Example Code

  • var number: Int = 30: Declares an Int variable number and sets it to 30.
  • var initializedPointer: UnsafePointer[Int] = UnsafePointer[Int].address_of(number): Initializes initializedPointer to point to the memory address of number.
  • print(initializedPointer[]): Dereferences initializedPointer to print the value 30.
var number: Int = 30
var initializedPointer: UnsafePointer[Int] = UnsafePointer[Int].address_of(number)
print(initializedPointer[])

Output

30

Safe Pointers in Mojo

Safe pointers in Mojo manage memory more securely compared to unsafe pointers. They automatically handle memory management and ensure that pointers don’t reference invalid or deallocated memory, reducing the risk of common errors.

safe pointers in mojo

Syntax

var pointer: Pointer[Type]

Explanation

  • var value: Int = 20: Declares an integer variable value and initializes it with 20.
  • var ptr: Pointer[Int] = Pointer[Int].init(value): Creates a safe pointer ptr that references the value variable using the init method.
  • print(ptr[]): Dereferences ptr to access and print the value at the memory address, which is 20.
var value: Int = 20
var ptr: Pointer[Int] = Pointer[Int].init(value)

print(ptr[])

Output

20

Referencing  in Safe Pointers

Referencing involves obtaining the memory address of a variable. In Mojo, when you reference a variable using a safe pointer, the memory management is handled securely, reducing risks.

Syntax

var number: Int = 10
var ptr: Pointer[Int] = Pointer[Int].init(number)

Example Code

  • var value: Int = 42: Initializes the integer variable value with 42.
  • var ptr: Pointer[Int] = Pointer[Int].init(value): Creates a pointer ptr that points to value.
  • ptr[] = 55: Dereferences the pointer and assigns 55 to the memory location ptr is pointing to, effectively updating the value of value to 55.
  • print("Updated value: \(value)"): This prints the updated value of value, which will now be 55.
  • print("Pointer address: \(ptr)"): Prints the address of the pointer ptr (the memory address of value).
var value: Int = 42
var ptr: Pointer[Int] = Pointer[Int].init(value)

ptr[] = 55  # Updating the value using the pointer

// Print the updated value
print("Updated value: \(value)")

Output

Updated value: 55

Dereferencing in Safe Pointers

Dereferencing means using a pointer to access the value stored at the memory address it points to. Safe pointers ensure that accessing the data doesn’t result in unsafe behaviour.

Syntax

var value = ptr[]         
ptr[] = 20  

Example Code

  • var value: Int = 100: Initializes the variable value with 100.
  • var ptr: Pointer[Int] = Pointer[Int].init(value): Creates a pointer ptr that points to value.
  • var dereferencedValue: Int = ptr[]: Dereferences the pointer ptr and assigns the value it points to (100) to dereferencedValue.
  • ptr[] = 200: Updates the value at the memory location pointed to by ptr (which is value) to 200.
var value: Int = 100
var ptr: Pointer[Int] = Pointer[Int].init(value)

var dereferencedValue: Int = ptr[]

// Print the dereferenced value
print("Dereferenced value: \(dereferencedValue)")

ptr[] = 200  # Updating the value using the pointer

// Print the updated value
print("Updated value: \(value)")

Output

Dereferenced value: 100
Updated value: 200

Declaration in Safe pointers

Declaring a safe pointer defines a variable that can hold the address of another variable. At this stage, the pointer does not yet point to any specific memory address

Syntax

var ptr: Pointer[Type]

Example Code

  • var value: Int = 50: Declares an Int variable value and assigns it the number 50.
  • var numberPointer: Pointer[Int] = Pointer[Int].init(value): Initializes numberPointer to point to the memory address of value.
  • print(numberPointer[]): Dereferences numberPointer to access and print the value it points to, which is 50.
var value: Int = 50
var numberPointer: Pointer[Int] = Pointer[Int].init(value)

print(numberPointer[])

Output

50

Initialization in Safe Pointer

Initializing a safe pointer assigns it a valid memory address. This is done by referencing the address of an existing variable

Syntax

var pointerName: Pointer[Type] = Pointer[Type].init(variable)

Example Code

  • var number: Int = 30: Declares an Int variable number and sets it to 30.
  • var initializedPointer: Pointer[Int] = Pointer[Int].init(number): Initializes initializedPointer to point to the memory address of number.
  • print(initializedPointer[]): Dereferences initializedPointer to print the value 30.
var number: Int = 30
var initializedPointer: Pointer[Int] = Pointer[Int].init(number)

print(initializedPointer[])

Output

30

Summary for Safe & Unsafe

OperationsSafe PointerUnsafe Pointer
Declarationvar ptr: Pointer[Type]var ptr: UnsafePointer[Type]
Initializationvar ptr: Pointer[Type] =Pointer[Type].init(variable)var ptr: UnsafePointer[Type] = UnsafePointer[Type].address_of(variable)
ReferencingPointer[Type].init(variable)UnsafePointer[Type].address_of(variable)
Dereferencingptr[]ptr[]

Pointer to Pointer

A pointer to pointer is a concept where a pointer holds the address of another pointer. This allows multi-level referencing, which means accessing or manipulating data through multiple levels of indirection.

pointer to pointer analogy

Syntax

var ptrToPtr: UnsafePointer[UnsafePointer[Int]]

Example Code

  • First var value: Int = 10: Declares an integer value and initializes it to 10.
  • then var ptr: UnsafePointer[Int] = UnsafePointer[Int].address_of(value): Creates a pointer ptr that holds the address of value.
  • Last but not least var ptrToPtr: UnsafePointer[UnsafePointer[Int]] = UnsafePointer[UnsafePointer[Int]].address_of(ptr): Declares ptrToPtr, which is a pointer to ptr. It holds the address of the ptr variable.
  • var accessedValue: Int = ptrToPtr[][]: Uses double dereferencing (ptrToPtr[][]) to access the original value (10) through both levels of pointers.
var value: Int = 10
var ptr: UnsafePointer[Int] = UnsafePointer[Int].address_of(value)
var ptrToPtr: UnsafePointer[UnsafePointer[Int]] = UnsafePointer[UnsafePointer[Int]].address_of(ptr)

// Accessing the value through ptrToPtr
var accessedValue: Int = ptrToPtr[][]
print(accessedValue)  // Added print statement to display the value

Output

10

Conclusion

In this article you learned what are pointers, the different types of pointers that mojo offers, along with the differences in those types as well as how you can declare, initialize, reference and dereference those pointers and how you can point a pointer to another pointer. To get a better idea of the syntax and background of this emerging language Mojo by Modular, follow our Mojo tutorials written by Syntax Scenarios.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top