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.

fn 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

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

Borrowing

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

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


fn 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.