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.
