Memory Model

Mojo has no garbage collector. Memory is managed through ownership, borrowing, and lifetimes — similar to Rust. Stack allocation is the default for value types. Heap allocation is explicit. You control where data lives and when it is freed.

Ownership transfer

A value can be transferred into a function by taking ownership with owned and the ^ operator. After the transfer, the original variable is no longer valid.

def main():
    # Stack-allocated: fixed size, fast, automatic cleanup
    var x: Int = 42

    # Ownership transfer with 'owned'
    var s1 = String("hello")
    take_ownership(s1^)
    # s1 is no longer valid here

def take_ownership(s: String):
    print(s)
    # s is destroyed when this function returns

Borrowing

Functions can borrow values without taking ownership. The default in def is an immutable borrow. Use mut for mutable borrows:

def print_length(s: String):
    print(len(s))  # immutable borrow (read-only access)


def append_exclaim(inout s: String):
    s += "!"       # mutable borrow

Stack vs Heap

  • Stack — fixed‑size types (Int, Float64, small structs). Allocation is a pointer bump. Deallocation is automatic on scope exit.
  • Heap — dynamic‑size types (String, buffers). Requires explicit or RAII‑based management. Slower to allocate.

Constraint

Write a function that takes a String by owned transfer. After calling it, try to use the original variable. Observe the compile‑time error that proves ownership moved.

Why It Matters

Every Python object lives on the heap with reference counting overhead. In Mojo, stack allocation costs zero — it's a pointer adjustment. No GC pauses, no reference count updates, no memory fragmentation. This is why Mojo can match C performance.