IMPORTANT: To view this page as Markdown, append `.md` to the URL (e.g. /docs/manual/basics.md). For the complete Mojo documentation index, see llms.txt.
Skip to main content
Version: 1.0.0
For the complete Mojo documentation index, see llms.txt. Markdown versions of all pages are available by appending .md to any URL (e.g. /docs/manual/basics.md).

Mojo ownership cheat sheet

Who owns a value, and how it passes between functions.

The model

Value ownership is fundamental to Mojo. Every value has exactly one owner, and how values move between owners runs through the whole language.

You encounter ownership in two situations: variables and function calls.

Variables can own or reference a value. Argument conventions describe how a function uses a value: read-only, reference, mutable, owned, produced, or consumed.

var owns, ref refers

var data: List[Int] = [1, 2, 3] # owns the list
ref view = data[0] # a 2nd name, no copy
view = 9 # writes through to data
print(data) # [9, 2, 3]

var always means "I own this." ref means "this is a view into someone else's value." A struct's var field owns its value and a struct type owns its fields.

Var assignment

A var assignment uses the right-hand side's policy: it determines whether the value is materialized, constructed, copied, or transferred. A call or expression returning a value constructs; one returning a reference copies out the referenced value.

You writeThe var takes ownership of
5.0 / "Hello" / [1, 2, 3] (a literal)a materialized literal
SomeType()a freshly constructed value
some_value (ImplicitlyCopyable)an implicit copy
some_value.copy() (Copyable)an explicit copy
some_value^ (Movable)the source's value, transferred
some_refa copy of the referenced value

A copy doesn't change a value's ownership. Only ^ moves the value to a new owner.

Argument conventions on self

ConventionMeaning
selfimm (immutable)
mut selfmodify the instance
out selfbuild it (in __init__())
deinit selfdestroy the instance
ref selfparametric mutability

Transfer ownership with ^

def exclaim(var s: String):
s += "!"
print(s)

var g = "Hello"
exclaim(g) # copy: g still usable
exclaim(g^) # transfer: g uninitialized
# print(g) # error: used after transfer

The var argument takes ownership of the original only with ^; a plain call implicitly copies (String is ImplicitlyCopyable), so g stays usable. Either value, the copy or the transferred original, ends its lifetime after the print (its last use). The same ^ drains a collection in a loop: for var x in items^ moves each element out.

Call sites: passing values

You writeInto a var arg
f(x)implicit copy (ImplicitlyCopyable only)
f(x.copy())explicit copy
f(x^)transfer; x uninitialized after

A borrowing argument (imm, mut, ref) has no ^ lever: you write f(x), and it views the value in place.

Origins on references

def first[T: Movable](ref xs: List[T]) -> ref[xs[0]] T:
return xs[0]

ref x = first(xs) # len(xs) known to be > 0

A ref return carries an origin so the compiler tracks where it points, whether it stays valid, and whether access is mutable. Values are destroyed at last use; a live ref keeps the value it refers to alive.

Argument conventions: the decision table

ConventionOwns it?Mutable?Caller keeps it?Reach for it when
(imm)nonoyesreading a value without changing it (the default)
mutnoyesyeschanging the caller's value in place
varyes (own copy)yesyes, unless ^you need a local, mutable copy
outyes (becomes the value)yesit is the resultreturning by name instead of ->
deinityes (consumes)yesnodestructors and the source of a move
refno (refers)parametricyesreturning or holding a reference with an origin

A convention sits before the argument name: def f(mut x: Int). With no convention, an argument is a read-only borrow: a view into a value you don't own. mut makes it a writable view.

Literals

var i = 5 # Int, machine width (default)
var i32: Int32 = 5 # SIMD[DType.int32, 1]

Literals are produced by the lexer, not built by a constructor. Each compile-time type (IntLiteral, FloatLiteral, StringLiteral) materializes into a runtime value. By default, integer literals are Int, floats are Float64, and strings are String. Use type annotations for specific types like Byte (UInt8), Int16, or BFloat16.

Trivial values

Trivial register types (Int, Float64, SIMD) are ImplicitlyCopyable with no destructor. A copy is a register copy; ^ is a no-op (the compiler warns transfer has no effect); there's nothing to destroy. The rules still apply, they just compile to register moves or nothing.

Mutability

Values aren't mutable or immutable. Access is.

NameMeaning
varalways mutable
refinherits the mutability of what it refers to

Lifecycle methods

MethodMeaning
__init__(out self, …)construct
__init__(out self, *, copy: Self)copy
__init__(out self, *, deinit move: Self)move
__deinit__(deinit self)destroy

Copy, move, and destructors can't raise. The var assignment table shows which one each assignment runs.

Non-owning views

var data: List[String] = ["a", "b", "c", "d", "e"]
var s = data[1:3] # a Span view, no copy: [b, c]
s[0] = "X" # writes through: data is [a, X, c, d, e]

var text = "Hello, World!"
var hi = text[codepoint=0:5] # a StringSpan view: "Hello"

A view is a non-owning window into a buffer someone else owns. Span views contiguous elements; StringSpan views UTF-8 text. Like ref, a view carries an origin, so the compiler keeps the source alive and tracks whether the view stays valid.

Returns: handing a value out

You writeWhat it does
return xcopy out (when ImplicitlyCopyable)
return x.copy()copy out
return x^transfer out
-> Treturn a value
-> ref[origin] Treturn a reference

Like a var assignment, return x copies; when x is at its last use the compiler moves it instead (you own it, so it can be moved).

No -> T^: the ^ goes on the returned value in return x^, not on the return type.