Mojo Ownership Model

Ownership is Mojo's system for tracking which part of your code is responsible for a piece of data at any given time. Every value has exactly one owner. When the owner goes out of scope, the value is automatically destroyed. This system eliminates memory leaks and use-after-free bugs without requiring a garbage collector.

The Library Book Analogy

  Library Book = a value in memory

  Owner = whoever checked out the book
  Rule: only one person can check out a book at a time

  When the borrower returns the book (goes out of scope),
  the library (memory system) reclaims it.

  Borrow (read-only): You can read the book but not mark it.
  Mutable borrow: You can annotate it, but only you can access it.
  Transfer (owned): You take the book permanently — old holder loses it.

The Three Ownership Modes

Mode        | Keyword    | What the callee can do
------------|------------|-------------------------------------------
Borrowed    | borrowed   | Read the value, cannot modify or destroy
Mutable ref | inout      | Read and modify, cannot destroy
Owned       | owned      | Full control — can modify and destroy

Borrowed: Read-Only Access

borrowed is the default for fn parameters. The function reads the value but the caller retains ownership. No copy is made — this is a zero-cost reference.

fn print_length(borrowed text: String):
    print(len(text))

fn main():
    var message = "Hello, Mojo!"
    print_length(message)   # 12
    print(message)          # Hello, Mojo! — still valid
Ownership stays with main():
  main() owns "Hello, Mojo!"
       │
       └──borrow──→ print_length() reads it
       │
  main() still owns it after call

inout: Mutable Reference

inout gives the function read and write access to the caller's variable. The caller still owns the value, but the function can modify it.

fn double_it(inout value: Int):
    value *= 2

fn main():
    var x = 7
    double_it(x)
    print(x)   # 14

owned: Transfer of Ownership

An owned parameter takes full ownership of the value. After the call, the original variable is no longer valid. Use the caret operator ^ (consume operator) to explicitly transfer ownership.

fn consume_string(owned s: String):
    print("Got:", s)
    # s is destroyed when this function ends

fn main():
    var greeting = "Hello"
    consume_string(greeting^)   # ownership transferred
    # print(greeting)  ← compile error: greeting was moved
Ownership transfer diagram:
  main() owns "Hello"
       │
       └──own──→ consume_string() now owns "Hello"
                        │
                        └── destroyed when function ends

  main() can no longer access "Hello"

Copy vs Move

When you assign one variable to another, Mojo either copies the value or moves (transfers) it, depending on the type.

Copyable Types

Types that implement the Copyable trait (like Int, Float64, Bool) create a fresh copy on assignment. Both variables remain valid.

fn main():
    var a = 42
    var b = a    # b is a copy of a
    b = 100
    print(a)     # 42 — unchanged
    print(b)     # 100

Movable Types

Types that implement Movable transfer their data on assignment using the caret operator. The source is invalidated.

fn main():
    var s1 = String("heavy data")
    var s2 = s1^    # s1's data moved to s2
    print(s2)       # heavy data
    # s1 is no longer usable
Copy:                 Move:
  a ──copy──→ b         s1 ──move──→ s2
  a still valid         s1 invalid
  two copies in memory  one copy in memory

Lifetime of a Value

fn main():
    var data = String("important")    ← data created, main() owns it
    print(data)                        ← borrowed for print
    {
        var temp = data^               ← ownership moved to temp
        print(temp)                    ← temp used here
    }                                  ← temp goes out of scope, destroyed
    # data is gone — cannot use it

Why This Matters for Performance

Python programs spend significant time in the garbage collector, which periodically scans memory to find and reclaim unused objects. This causes unpredictable pauses. Mojo knows exactly when each value is no longer needed at compile time and destroys it immediately — no pauses, no overhead, deterministic behavior.

Python memory lifecycle:
  Create → use → ... (GC decides when to collect) → destroy
  GC runs unpredictably, can pause your program

Mojo memory lifecycle:
  Create → use → out of scope → IMMEDIATELY destroyed
  No GC, no pauses, no surprises

Key Takeaways

Every Mojo value has one owner at a time. borrowed gives read-only access without copying or transferring ownership. inout gives mutable access while the caller retains ownership. owned transfers full ownership using the ^ consume operator. Copying duplicates a value; moving transfers it and invalidates the source. Mojo's ownership system eliminates garbage collection pauses and prevents entire classes of memory bugs at compile time.

Leave a Comment

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