Mojo By Example: A Comprehensive Introduction to the Mojo Programming Language (ruhati.net)
title:
style: nestedList # TOC style (nestedList|nestedOrderedList|inlineFirstLevel)
minLevel: 0 # Include headings from the specified level
maxLevel: 0 # Include headings up to the specified level
includeLinks: true # Make headings clickable
debugInConsole: false # Print debug info in Obsidian consoleSIMD
- Single Instruction multiple data, stores.
- SIMD allows a single instruction to be executed across the multiple data elements of the vector.
SIMD[dtype, size]dtype = Dtype.int/ Dtype.float64 ....- size = The size of the SIMD vector to be positive and a power of 2.
Types
Basic numerical types defined in Mojo are actually SMID’s of size 1.
Int8 = Scalar[DType.int8]
Float64 = SMID[DType.float64, 1]
Scalar = SIMD[size=1]
Variables
- declared -
var k = 3 - undeclared -
k = 3 - declared follow lexical scoping - with in a function declared variable in inner scope is not accessible outside of inner scope.
- undeclared follow python function scope.
- all varaiables are strongly typed, so no different datatype assignment.
Value ownership
^transfer operator: to move the value, unless the value is a trivial type (likeInt) or a newly-constructed, “owned” value.- owned - has unique mutable access to this variable
- Technically, the
ownedkeyword does not guarantee that the received value is the original value—it guarantees only that the function gets unique ownership of a value (enforcing value semantics). This happens in one of three ways: - The caller passes the argument with the
^transfer operator, which ends the lifetime of that variable (the variable becomes uninitialized) and ownership is transferred into the function without making a copy of any heap-allocated data. - The caller does not use the
^transfer operator, in which case, the value is copied into the function argument and the original variable remains valid. (If the original value is not used again, the compiler may optimize away the copy and transfer the value). - The caller passes in a newly-created “owned” value, such as a value returned from a function. In this case, no variable owns the value and it can be transferred directly to the callee.
- Technically, the
- inout - shared ownership, modifications reflect across scope.
- no default values for inout
- in subsequent fn no inout on borrowed variables
- borrowed - no modifications allowed
- Small values like
Int,Float, andSIMDare passed directly in machine registers instead of through an extra indirection.
- Small values like
Alias
compile time temporary value that cant change at runtime
Struct
- @value will synthesize the essential lifecycle methods so your object provides full value semantics. Specifically, it generates the
__init__(),__copyinit__(), and__moveinit__() - immediately it will be compatible with List etc.
- methods inside structs are fn.
Functions
fn
fn function_name[p:dtype #parameter](k:dtype #argument) -> dtype:
- default ownership - borrowed, read-only
- default only supports declared variables.
- inputs and outputs are also strongly typed
- parameter is compile time variable, and argument is runtime variable.
- parameter should be available by compile time
def
def function_name():
- default ownership - borrowed, but if the function mutates the argument, it makes a mutable copy
- default supports declared and undeclared variables.
Loops
for
- iterating through for loop creates var variable and assigns reference to object, use
[]to access value.
Decorators
@parameter
- add the
@parameterdecorator on anifstatement or on a nested function to run that code at compile time.
Pointers
- Creates an indirect reference to a location in memory.
UnsafePointercan dynamically allocate and free memory, or to point to memory allocated by some other piece of code.- User responsible for ensuring that memory gets allocated and freed correctly
pointee : value pointed to by a pointer. dereference : retrieve or update pointee using
ptr[]
Lifecycle
Check LinkedList for Memory lifecycle example.
- Uninitialized:
var ptr: UnsafePointer[Int] - Null/invalid pointer → address 0 :
var ptr: UnsafePointer[Int]() - alloc → allocates n dtype contiguous blocks of memory:
var ptr = UnsafePointer[dtype].alloc(n)- after allocation memory is still uninitialized, dereferencing causes undefined behavior
- Initialization:
initialize_pointee_copy(ptr, value) -- copy or initalize_pointee_move(ptr, value^) -- move or ptr = UnsafePointer[Int].address_of(value) -- assign- once initialized we can dereference it
- Dangling pointer → pointer after freeing the allocated memory
ptr.free()- addr still points to previous location, but that memory is no longer allocated to this pointer.
- dereferencing causes undefined behavior

Methods
-
destroy_pointee: Destroy the pointed-to value. -
move_from_pointee: Move the value at the pointer out. -
initialize_pointee_move: Emplace a new value into the pointer location, moving fromvalue. -
initialize_pointee_copy: Emplace a copy ofvalueinto the pointer location. -
move_pointee: Moves the valuesrcpoints to into the memory location pointed to bydest - free : frees memory allocated by the pointer, will not call destructors on its values, use destroy pointee or any other move methods from above.
DTypePointer: handling numeric data
A DTypePointer is an unsafe pointer that supports some additional methods for loading and storing numeric data. Like the SIMD type, it’s parameterized on DType as described in SIMD and DType.
DTypePointerworks with are trivial types, so no copy or move or destroy methods required.- Has
alloc(),free()andaddress_of() - use subscript notation
[]or use theload()andstore()methods to access values. - Use
simd_strided_load()andsimd_strided_store()methods for efficient SIMD operations
Note: DTypePointer is depreciated. UnsafePointer can handle most things that DTypePointer does except for SIMD operations. In future SIMD type supports all these operations, so UnsafePointer can just use them.
Safety
Unsafe pointers are unsafe for several reasons:
- Memory management is up to the user.
UnsafePointerandDTypePointervalues are nullable or can be initialized.- Mojo doesn’t track lifetimes for the data pointed to by an
UnsafePointer, so managing memory and knowing when to destroy objects is user responsibility.
Reference
The Reference type is essentially a safe pointer type.
Referenceis non-nullable. A reference always points to something.- No alloc or free on
Reference— only point to an existing value. Referenceonly refers to a single value. You can’t do pointer arithmetic with aReference.Referencehas an associated lifetime, which connects it back to an original, owned value. The lifetime ensures that the value won’t be destroyed while the reference exists.
The Reference type shouldn’t be confused with the immutable and mutable references used with the borrowed and inout argument conventions. Those references do not require explicit dereferencing, unlike a Reference or UnsafePointer.
#### Arc Reference-counted smart pointers