# Mojo Manual
> The Mojo programming manual, covering everything from getting started to advanced Mojo topics.
Version: 1.1.0
This file contains all documentation content in a single document following the llmstxt.org standard.
## Mojo language basics
This page provides an overview of the Mojo language.
If you know Python, then a lot of Mojo code looks familiar. However, Mojo
incorporates features like static type checking, memory safety, next-generation
compiler technologies, and more. As such, Mojo also has a lot in common with
languages like C++ and Rust.
If you prefer to learn by doing, follow the [Get started with
Mojo](/docs/manual/get-started/) tutorial.
On this page, we'll introduce the essential Mojo syntax, so you can start
coding quickly and understand other Mojo code you encounter. Subsequent
sections in the Mojo Manual dive deeper into these topics, and this page links
to them as appropriate.
Let's get started! 🔥
:::note
Mojo is a young language that's still [evolving](/docs/roadmap/). As such, Mojo
is currently **not** meant for beginners. Even this basics section assumes some
programming experience. However, throughout the Mojo Manual, we try not to
assume experience with any particular language.
:::
## Hello world
Here's the traditional "Hello world" program in Mojo:
```mojo
def main():
print("Hello, world!")
```
Every Mojo program must include a function named `main()` as the entry point.
We'll talk more about functions soon, but for now it's enough to know that
you can write `def main():` followed by an indented function body.
The [`print()`](/docs/std/io/io/print/) function does what you'd expect,
printing its arguments to the standard output.
This page omits `def main():` for many brief examples.
To test these, add them to a `main()` function.
## Variables
In Mojo, you can declare a variable using the `var` keyword:
```mojo
def main():
var x = 10
var y = x * x
print(y)
```
You can also explicitly declare the variable type, with or without an
assignment:
```mojo
def main():
var x: Int = 10
var sum: Int
sum = x + x
```
Mojo variables are statically typed:
that is, Mojo sets a variable's type at compile time, and the type doesn't
change at runtime.
If you don't specify a type, Mojo uses the type of the first value assigned to
the variable.
```mojo
var x = 10
x = "Foo" # Error: cannot implicitly convert 'StringLiteral["Foo"]' value to 'Int'
```
For more details, see the page about
[variables](/docs/manual/variables/).
## Blocks and statements
Define code blocks such as functions, conditions, and loops
with a colon followed by indented lines. For example:
```mojo
def loop():
for x in range(5):
if x % 2 == 0:
print(x)
```
You can use any number of spaces or tabs for your indentation (we prefer 4
spaces).
All code statements in Mojo end with a newline. The Mojo compiler is fairly
lenient in allowing extra line breaks. As a rule of thumb, you can always break
statements between a pair of parentheses (`()`), square brackets (`[]`), or
curly braces (`{}`):
```mojo
matrix_multiply(
matrix_a,
matrix_b,
result_matrix
)
```
You can add parentheses to continue a statement across lines:
```mojo
var long_text = (
"This is a long line of text that is a lot easier to read if"
" it is broken up across two lines instead of one long line."
)
```
Mojo combines adjacent string literals, so `long_text` ends up with
a single, combined string.
For more information on loops and conditional statements, see
[Control flow](/docs/manual/control-flow/).
## Functions
Define Mojo functions with the `def` keyword. For example, the
following uses the `def` keyword to define a function named `greet()`
that requires a single [`String`](/docs/std/collections/string/string/String/)
argument and returns a `String`:
```mojo
def greet(name: String) -> String:
return "Hello, " + name + "!"
```
## Code comments
You can create a one-line comment using the hash `#` symbol:
```mojo
# This is a comment. The Mojo compiler ignores this line.
```
Comments may also follow some code:
```mojo
var message = "Hello, World!" # This is also a valid comment
```
Enclose API documentation comments in triple quotes. For example:
```mojo
def print(x: String):
"""Prints a string.
Args:
x: The string to print.
"""
...
```
Documenting your code with these kinds of comments (known as "docstrings")
is a topic we've yet to fully specify, but you can generate an API reference
from docstrings using the [`mojo doc` command](/docs/cli/doc/).
:::note
Technically, docstrings aren't _comments_, they're a special use of Mojo's
syntax for multi-line string literals. For details, see
[String literals](/docs/manual/types/#string-literals) in the page on
[Types](/docs/manual/types/).
:::
## Structs
You can build high-level abstractions for types (or "objects") as a `struct`.
A `struct` in Mojo is similar to a `class` in Python: they both support
methods, fields, operator overloading, decorators for metaprogramming, and so
on. However, Mojo structs are completely static—the compiler binds them at
compile time, so they don't allow dynamic dispatch or any runtime changes to
the structure.
(Mojo will also support Python-style classes in the future.)
For example, here's a basic struct:
```mojo
struct MyPair(Copyable):
var first: Int
var second: Int
def __init__(out self, first: Int, second: Int):
self.first = first
self.second = second
def __init__(out self, *, copy: Self):
self.first = copy.first
self.second = copy.second
def dump(self):
print(self.first, self.second)
```
And here's how you can use it:
```mojo
def use_mypair():
var mine = MyPair(2, 4)
mine.dump()
```
The `MyPair` struct contains two special methods, `__init__()`, the initializer,
and `__init__(out self, *, copy: Self)`, the copy initializer.
_Lifecycle methods_ like this control how Mojo creates, copies, moves, and
destroys a struct.
For most simple types, you don't need to write the lifecycle methods. You can
use the [`@fieldwise_init`](/docs/reference/decorators/fieldwise-init/)
decorator to generate the boilerplate field-wise initializer for you, and Mojo
synthesizes copy and move initializers if you ask for them with trait
conformance. So you can simplify the `MyPair` struct to this:
```mojo
@fieldwise_init
struct MyPair(Copyable):
var first: Int
var second: Int
def dump(self):
print(self.first, self.second)
```
For more details, see the page about
[structs](/docs/manual/structs/).
### Traits
A trait is like a template of characteristics for a struct. If you want to
create a struct with the characteristics defined in a trait, you must implement
each characteristic (such as each method). Each characteristic in a trait is a
"requirement" for the struct, and when your struct implements all of the
requirements, it "conforms" to the trait.
Using traits allows you to write parameterized functions that can accept any
type that conforms to a trait, rather than accepting only specific types.
For example, here's how you can create a trait:
```mojo
trait SomeTrait:
def required_method(self, x: Int): ...
```
The three dots following the method signature are Mojo syntax indicating that
the method has no implementation.
Here's a struct that conforms to `SomeTrait`:
```mojo
@fieldwise_init
struct SomeStruct(SomeTrait):
def required_method(self, x: Int):
print("hello traits", x)
```
Then, here's a function that uses the trait as an argument type (instead of the
struct type):
```mojo
def fun_with_traits[T: SomeTrait](x: T):
x.required_method(42)
def use_trait_function():
var thing = SomeStruct()
fun_with_traits(thing)
```
You'll see traits used in a lot of APIs provided by Mojo's standard library. For
example, Mojo's collection types like [`List`](/docs/std/collections/list/List/)
and [`Dict`](/docs/std/collections/dict/Dict/) can store any type that conforms
to the [`Movable`](/docs/std/traits/movable/Movable/) trait (`Dict` keys must
also conform to [`KeyElement`](/docs/std/collections/dict/#keyelement)).
You can specify the type when you create a collection:
```mojo
var my_list = List[Float64]()
```
:::note
You're probably wondering about the square brackets on `fun_with_traits()`.
These aren't function _arguments_ (which go in parentheses); these are
compile-time _parameters_, which we'll explain in the next section.
:::
Without traits, the `x` argument in `fun_with_traits()` would have to declare a
specific type that implements `required_method()`, such as `SomeStruct` (but
then the function would accept only that type). With traits, the function can
accept any type for `x` as long as it conforms to (it "implements") `SomeTrait`.
Thus, `fun_with_traits()` is a "parameterized function" because it accepts a
_generalized_ type instead of a specific type.
For more details, see the page about [traits](/docs/manual/traits/).
## Parameterization
In Mojo, a parameter is a compile-time variable that becomes a runtime
constant, and you declare it in square brackets on a function or struct.
Parameters allow for compile-time metaprogramming, which means you can generate
or modify code at compile time.
Many other languages use "parameter" and "argument" interchangeably, so be aware
that when we say things like "parameter" and "parameterized function," we're
talking about these compile-time parameters. In contrast, a function "argument"
is a runtime value that you declare in parentheses.
Parameterization is a complex topic that the
[Metaprogramming](/docs/manual/metaprogramming/) section covers in much more
detail, but we want to break the ice just a little bit here. To get you started,
let's look at a parameterized function:
```mojo
def repeat[count: Int](msg: String):
# evaluate the following for loop at compile time
comptime for i in range(count):
print(msg)
```
This function has one parameter of type [`Int`](/docs/std/simd/#int) and
one argument of type `String`. To call the function, you need to specify both
the parameter and the argument:
```mojo
def call_repeat():
repeat[3]("Hello")
# Prints "Hello" 3 times
```
By specifying `count` as a parameter, the Mojo compiler can optimize the
function because this value can't change at runtime. And the `comptime` keyword
in the code tells the compiler to evaluate the `for` loop at compile time, not
runtime.
The compiler effectively generates a unique version of the `repeat()` function
that repeats the message only 3 times. This makes the code more performant
because there's less to compute at runtime.
Similarly, you can define a struct with parameters, which effectively allows
you to define variants of that type at compile time, depending on the parameter
values.
For more detail on parameters, see the section on
[Metaprogramming](/docs/manual/metaprogramming/).
## Python integration
Mojo supports the ability to import Python modules as-is, so you can leverage
existing Python code right away.
For example, here's how you can import and use NumPy:
```mojo
from std.python import Python
def main() raises:
var np = Python.import_module("numpy")
var ar = np.arange(15).reshape(3, 5)
print(ar)
print(ar.shape)
```
You must have the Python module (such as `numpy`) installed in the environment
where you're using Mojo.
For more details, see the page on
[Python integration](/docs/manual/python/).
## Next steps
Hopefully this page has given you enough information to start experimenting with
Mojo, but this is only touching the surface of what's available in Mojo.
If you're in the mood to read more, continue through each page of this
Mojo Manual—the next page from here is [Functions](/docs/manual/functions/).
Otherwise, here are some other resources to check out:
- See [Get started with Mojo](/docs/manual/get-started/) for a hands-on
tutorial that gets you up and running with Mojo.
- If you want to experiment with some code, clone [our GitHub
repo](https://github.com/modular/modular/) to try our code examples:
```sh
git clone https://github.com/modular/modular.git
cd modular/Mojo/examples
```
- To see all the available Mojo APIs, check out the [Mojo standard library
reference](/docs/std/).
---
## Using Mojo's C foreign function interface to call C libraries
When you need functionality that's already available in a C library, you
can call it directly from your Mojo code. Many libraries for graphics,
databases, hardware control, signal processing, and scientific computing
expose C APIs.
Mojo emits a direct native call, with no translation layer or extra runtime
overhead. A C call from Mojo runs as fast as handwritten C.
## C number types
C integer types don't have fixed sizes. Their sizes depend on the target
platform and its C *ABI*. For example, `int` is commonly 32 bits, while
`long` is 64 bits on Linux and macOS but 32 bits on Windows.
An ABI (application binary interface) defines how machine code passes
arguments, returns values, and lays out data in memory.
Use the `std.ffi` module's type aliases when working with C APIs. They
match the target platform's C ABI, so you don't need to worry about
platform-specific size differences.
See the [C type reference](#c-type-reference) at the end of this page for a
list of `std.ffi` type aliases and their equivalent Mojo types.
## Call libc functions
`libc` is the C standard library. It provides functions for memory
allocation, string manipulation, file I/O, and other common tasks. Mojo
calls libc functions with `external_call()`. Mojo resolves the symbol for
you, so you don't need to add anything to your build.
Import `external_call` from `std.ffi`. Parameterize it with the function
name and return type. Then pass the function arguments in parentheses.
Mojo infers the argument types from the values you pass, so there's nothing
else to declare:
```mojo
def external_call[
callee: StaticString,
return_type: RegisterPassable,
*types: AnyType,
num_fixed_args: OptionalReg[Int] = None,
](*args: *types) -> return_type
```
The following example calls the C `abs()` function, which returns the
absolute value of an integer:
```mojo
from std.ffi import external_call, c_int
def main():
# int abs(int n);
var n = external_call["abs", c_int](c_int(-42))
print(t"Absolute value is 42: {n == 42}") # True
```
`c_int` is the `std.ffi` alias for C's `int`. It's 32 bits on every
platform Mojo targets. Its Mojo counterpart is `Int32`.
### Call variadic C functions
A *variadic* C function takes a variable number of arguments, like
`printf()` and `snprintf()`. Pass `num_fixed_args` with the number of
arguments declared before the `...`:
```mojo
from std.ffi import external_call, c_char, c_int, c_size_t
def main():
# int snprintf(char *buf, size_t size, const char *fmt, ...);
# Three fixed arguments, so num_fixed_args=3.
var buf = Array[c_char, 64](uninitialized=True)
var written = external_call["snprintf", c_int, num_fixed_args=3](
buf.unsafe_ptr(),
c_size_t(64),
"score: %d/%d".as_c_string_span(),
c_int(7),
c_int(10),
)
print(t"wrote {written}: {String(unsafe_from_utf8_ptr=buf.unsafe_ptr())}")
```
Without `num_fixed_args`, Mojo treats every argument as fixed. Some ABIs
pass variadic arguments differently from fixed ones, so the call can work
on one target and break on another.
## Use shared libraries
An `OwnedDLHandle` owns a handle to a dynamically linked library with RAII
semantics. Use it to load shared libraries and retrieve functions as Mojo
callables, so you can work with libraries such as SQLite, libcurl, camera
SDKs, GPU vendor libraries, and other native libraries.
Library names differ by platform, so use `platform_map()` to select the right
one at compile time:
```mojo
from std.ffi import OwnedDLHandle, c_double
from std.sys.info import platform_map
comptime LIBM = platform_map["libm", linux="libm.so.6", macos="libm.dylib"]()
def main() raises:
var lib = OwnedDLHandle(LIBM)
var sqrt = lib.get_function[c_double]("sqrt")
print(sqrt(c_double(4.0))) # Prints: 2.0
# Library automatically closed when lib goes out of scope
```
If `platform_map()` has no value for the target, it raises a compilation
error. It won't fall through to a library name for another platform.
### Library names
Pass the library as any `os.PathLike`, such as a `String` or a `Path`. Mojo
resolves the name at runtime. Use the bare name (`libm.dylib`) when the
library is on the system search path, or a full path
(`path/to/libm.dylib`) when it isn't.
On Linux, use the ABI-versioned runtime name, such as `libm.so.6`, instead
of the unversioned `libm.so`. An ABI version doesn't necessarily match the
library's release version. For example, libcurl 8.21 still uses
`libcurl.so.4`.
The unversioned name belongs to the development package, where the static
linker consumes it for options such as `-lm`. It's often a linker script
rather than a library, so passing it to `dlopen` can fail with an
`invalid ELF header` error. Find the shared libraries the dynamic linker
knows about with:
```bash
ldconfig -p | grep libcurl
```
macOS uses one name for both purposes. `libcurl.dylib` is both what you
link against and what you load.
If you omit the library name, `OwnedDLHandle()` opens the current process.
This is another way to call libc functions and other symbols already linked
into your program.
### Availability checks
`OwnedDLHandle` loads libraries at runtime, so the library must be available
when your program runs. If it can't be found, loading fails:
```mojo
comptime LIBCURL = platform_map[
"libcurl", linux="libcurl.so.4", macos="libcurl.dylib"
]()
try:
var lib = OwnedDLHandle(LIBCURL)
# use the optional feature
except:
# fall back
```
You can guard against missing functions with `check_symbol()`. Use it to
test for optional, versioned, or platform-specific features. The check
works for both functions and exported globals:
```mojo
comptime LIBM = platform_map[
"libm", linux="libm.so.6", macos="libm.dylib"
]()
var lib = OwnedDLHandle(LIBM)
if lib.check_symbol("exp10"):
var exp10 = lib.get_function[c_double]("exp10")
print(exp10(c_double(2.0))) # 100.0
else:
print("exp10 not found in libm")
```
### Retrieve functions by name
`get_function()` looks up a library function by name and returns a callable.
Parameterize it with the C function's return type. Here's a curses example:
```mojo
# WinPtr is a pointer to a curses window struct
var wgetch = lib.get_function[c_int]("wgetch")
# ... later
_ = wgetch(win) # blocks until a key is pressed.
```
You don't declare the argument types. Mojo infers them from the values you
pass at each call, and forwards them using the C calling convention.
Missing symbols raise errors.
## Passing pointers {#pointers}
Many C APIs work with pointers. Mojo represents raw pointers with
`Pointer[T]`, where `T` is the pointed-to type. When a C API expects a
`void*`, use `.unsafe_bitcast[NoneType]()` to produce an `OpaquePointer`.
- Use `Pointer(to=value)` to get a pointer to a Mojo value.
- Use `.unsafe_bitcast[U]()` to reinterpret a pointer as another
pointer type.
For example:
```mojo
var value: c_int = 42
var p = Pointer(to=value) # Pointer to a C int
var opaque: OpaquePointer[origin_of(value)] = p.unsafe_bitcast[NoneType]()
```
### Typed pointers
C functions often write results through a pointer you provide, rather than
returning them. Pass `Pointer(to=value)` and C fills in the value. An `imm`
function argument won't work, and, worse, it fails quietly, leaving the
value unchanged. Use the `mut` convention or copy the value into a local
`var` before your call.
This example passes a Mojo floating-point number to C's `frexp`, which
splits it into a mantissa and an exponent:
```mojo
from std.ffi import external_call, c_double, c_int
def main():
# double frexp(double x, int *exp);
# Returns the mantissa and writes the exponent through the pointer.
var exponent: c_int = 0
var mantissa = external_call["frexp", c_double](
c_double(12.0), Pointer(to=exponent)
)
print(t"12.0 = {mantissa} * 2^{exponent}") # 0.75 * 2^4
```
### Opaque pointers {#opaque-pointers}
The C standard library provides `qsort`, a general-purpose sorting
function.
`qsort` sorts its array in place. You provide a pointer to that array, its
number of elements, the element size, and a comparison function. Whenever
`qsort` compares two elements, it calls your Mojo-native comparison
function.
The comparison function must be *thin*. That is, it can't capture any Mojo
state as a closure. You must mark it with `abi("C")`, allowing `qsort` to
call it across the FFI boundary.
The following example sorts a list of C integers. The `compare()`
function receives two `void*` pointers, casts them back to `c_int*`,
and returns the comparison result:
```mojo
from std.ffi import external_call, c_int, c_size_t
from std.sys import size_of
def compare(
a: OpaquePointer[mut=False, _],
b: OpaquePointer[mut=False, _],
) abi("C") -> c_int:
var a_value = a.unsafe_bitcast[c_int]()[]
var b_value = b.unsafe_bitcast[c_int]()[]
# `qsort` only needs to know which value is larger. Compare the values
# instead of subtracting them. Large differences can overflow, producing
# the wrong comparison result and sorting the values incorrectly.
if a_value < b_value:
return c_int(-1)
return c_int(a_value > b_value)
def main() raises:
var numbers: List[c_int] = [5, 2, 9, 1, 5, 6]
var count = c_size_t(len(numbers))
var size = c_size_t(size_of[c_int]())
external_call["qsort", NoneType](
numbers.unsafe_ptr(),
count,
size,
compare,
)
print("Sorted numbers:", numbers) # [1, 2, 5, 5, 6, 9]
```
## Passing structs
Struct pointers allow Mojo and C APIs to exchange structured data that goes
beyond simple values. For example, `clock_gettime()` writes the system's
monotonic time into a C `struct timespec`.
To read that data from Mojo, define a struct with a C-compatible layout and
pass a pointer to it:
```mojo
from std.ffi import external_call, c_int, c_long
from std.sys.info import platform_map
@fieldwise_init
struct CTimeSpec(RegisterPassable): # Matches C's struct timespec.
# CLOCK_MONOTONIC differs by platform
comptime monotonic = c_int(
platform_map["CLOCK_MONOTONIC", linux=1, macos=6]()
)
var tv_sec: c_long
var tv_nsec: c_long
@staticmethod
def monotonic_nanos() raises -> c_long:
var time_spec = Self(0, 0)
if (
external_call["clock_gettime", c_int](
Self.monotonic,
Pointer(to=time_spec),
)
!= 0
):
raise Error("clock_gettime failed")
return time_spec.tv_sec * 1_000_000_000 + time_spec.tv_nsec
def main() raises:
print(t"Monotonic time: {CTimeSpec.monotonic_nanos()} ns")
```
### C-compatible structs
C-compatible types are ordinary structs with two requirements:
- They conform to `RegisterPassable`.
- They contain only C-compatible fields.
Field order matters. Declare your fields in the same order as the C struct
you're mirroring. Mojo uses the corresponding C layout, including padding
required for [field alignment](/docs/reference/decorators/align/):
```mojo
# Mirrors C `div_t`: two ints, 8 bytes total.
@fieldwise_init
struct DivT(RegisterPassable):
var quot: c_int
var rem: c_int
def main() raises:
var proc = OwnedDLHandle() # No path: opens the current process
var div = proc.get_function[DivT]("div")
var d = div(c_int(7), c_int(3))
print(t"div(7, 3): quot {d.quot} rem {d.rem}") # 2 1
```
### Passing lists, arrays, and spans
A Mojo `List[T]` stores its elements contiguously in memory, just like C
arrays. You pass a list to C as a pointer plus a length, as shown in the
[`qsort` example](#opaque-pointers).
Mojo list pointers are fragile. Operations that grow the list, such as
`append()`, may move its storage and leave an earlier pointer stale. So get
the pointer fresh, right before you use it, after any change to the list.
`Span[T]` is Mojo's built-in pointer-plus-length pair. It wraps a pointer
to contiguous memory and stores a length. This gives you built-in bounds
checking and safe iteration.
`Array[T, length]` is Mojo's fixed-size array. It owns its elements inline,
so Mojo cleans it up and C can fill it through a pointer plus a length.
Both `Span` and `Array` are safe to pass to and from C by pointer. Add a
length to calls where C needs one.
The following example allocates a 256-byte `Array`, passes it to
C's `getcwd()`, wraps the filled bytes in a `Span`, and converts them
to a Mojo `String`:
```mojo
from std.ffi import external_call, c_char, c_size_t
def main() raises:
# char *getcwd(char *buf, size_t size); C fills a buffer that Mojo owns.
comptime CAPACITY = 256
var buf = Array[c_char, CAPACITY](uninitialized=True)
var filled = external_call[
"getcwd", Optional[Pointer[c_char, origin_of(buf)]]
](buf.unsafe_ptr(), c_size_t(CAPACITY))
if not filled:
raise Error("getcwd failed")
# C reports no length, so ask for it, then wrap the bytes in a `Span`.
var length = external_call["strlen", c_size_t](buf.unsafe_ptr())
var span = Span(
unsafe_ptr=buf.unsafe_ptr().unsafe_bitcast[Byte](), length=Int(length)
)
print(t"{len(span)} bytes: {String(from_utf8=span)}")
```
`Span`s work with both Mojo and C memory:
- If you wrap a Mojo-owned buffer, the `Span` keeps it alive.
- If you wrap a C-owned buffer, such as memory from `malloc()`, the `Span`
doesn't free it. You must free C-owned memory with C.
## Passing strings
C strings are null-terminated byte arrays (`char*`). Mojo strings are
length-prefixed UTF-8.
### Convert a Mojo string to a C string
Call `as_c_string_span()` on a `String` to ensure null termination, and get
a type safe `CStringSpan` back.
```mojo
name.as_c_string_span()
```
The source string must be mutable because `as_c_string_span()` may append
a terminating zero byte. It may also move the string's buffer, so call it
once and reuse the result.
### Convert a C string to a Mojo string
Use `String(unsafe_from_utf8_ptr=...)` to copy a null-terminated C string
into a Mojo string:
```mojo
# Copies the bytes; uses `strlen()`.
String(unsafe_from_utf8_ptr=c_string_ptr)
```
When you already know the length, you can wrap the C bytes in a
non-copying, non-owning `Span[Byte]` and covert that to a Mojo `String`.
For example, C's `strdup()` allocates and returns a copy of a string. You
can wrap its result in a `Span`, convert the bytes to a Mojo string, then
free the C-owned memory:
```mojo
var name: String = "Echo"
var cptr = external_call[
"strdup", Optional[Pointer[c_char, MutUntrackedOrigin]]
](name.as_c_string_span())
if cptr:
var ptr = cptr.value()
# Ask C for the length. A Mojo string's `byte_length()` measures the
# Mojo side, which says nothing about the buffer C returned.
var length = external_call["strlen", c_size_t](ptr)
var span = Span(unsafe_ptr=ptr.unsafe_bitcast[Byte](), length=Int(length))
print(String(from_utf8=span)) # or from_utf8_lossy or unsafe_from_utf8
external_call["free", NoneType](ptr.unsafe_bitcast[NoneType]()) # free it
```
### Convert Mojo string literals to C strings
String literals can be passed to C APIs that expect a null-terminated
`char*`. Call `as_c_string_span()` to access the C string:
```mojo
"libm.so.6".as_c_string_span()
```
Mojo performs the conversion at compile time and embeds the null-terminated
string in the compiled program.
## Memory management
Mojo tracks the lifetime of its own memory. C memory has no Mojo value
behind it, so there's nothing for Mojo to track. Every allocation that
crosses the boundary still belongs to one side, and that side remains
responsible for freeing it:
- Free C memory with C's `free()`.
- Let Mojo handle its own memory, except for unsafe allocations.
### Allocate C memory
C allocators such as `malloc` return C-owned memory. `malloc` returns
null when the allocation fails. Wrap the return type in `Optional`:
```mojo
from std.ffi import external_call, c_size_t
def create_buffer(
n: c_size_t,
) -> Optional[Pointer[UInt8, MutUntrackedOrigin]]:
return external_call[
"malloc", Optional[Pointer[UInt8, MutUntrackedOrigin]]
](n)
def main() raises:
var buf = create_buffer(c_size_t(16))
if not buf:
raise Error("malloc failed")
var ptr = buf.value()
ptr[unsafe_offset=0] = 42
print(ptr[unsafe_offset=0]) # 42
external_call["free", NoneType](ptr.unsafe_bitcast[NoneType]())
```
`MutUntrackedOrigin` tells Mojo not to reason about this pointer's lifetime.
It's the opposite of every other origin on this page. Instead of tying the
pointer to an owner, it says that no Mojo value owns *this* memory. You're
responsible for keeping it valid and freeing it. You must free it with C's
memory management functions, such as `free`.
### Free C memory automatically
Pairing every `malloc()` with a matching `free()` by hand is easy to get
wrong. A context manager can manage the allocation and release it for you.
When the following block exits, `__exit__()` calls `free()`, even after a
raised error:
```mojo
from std.ffi import external_call, c_size_t
struct CBuffer:
var ptr: Pointer[UInt8, MutUntrackedOrigin]
var size: c_size_t
def __init__(out self, n: c_size_t) raises:
self.size = n
var allocated = external_call[
"malloc", Optional[Pointer[UInt8, MutUntrackedOrigin]]
](n)
if not allocated:
raise Error("malloc failed")
self.ptr = allocated.value()
def __enter__(self) -> Pointer[UInt8, MutUntrackedOrigin]:
return self.ptr
def __exit__(self):
external_call["free", NoneType](self.ptr.unsafe_bitcast[NoneType]())
def main() raises:
with CBuffer(c_size_t(1024)) as buf:
buf[unsafe_offset=0] = 42
print(buf[unsafe_offset=0]) # 42
# The buffer is freed here.
```
### Null returns
C uses null pointers to mean "nothing" or "failed." A Mojo `Pointer` can't
be null, so wrap any "maybe null" return in `Optional`. The `malloc`
examples you just saw showed this pattern.
`Optional`'s empty case adds nothing to the call and costs nothing to pass:
```mojo
from std.ffi import external_call, c_char
def main() raises:
var name: String = "PATH"
var found = external_call[
"getenv", Optional[Pointer[c_char, MutUntrackedOrigin]]
](name.as_c_string_span())
if found:
print(String(unsafe_from_utf8_ptr=found.value()))
else:
print(t"{name} is not set")
```
Declaring an unwrapped, non-optional `Pointer` would compile. It would also
treat C's null as a valid pointer. Dereferencing results in undefined
behavior and will typically crash your program.
### Keeping Mojo values alive
Pointers into Mojo memory carry an origin that tracks the value's lifetime.
When you derive a pointer from a variable, Mojo keeps the variable alive
while the pointer is live. It rejects code that would let the variable die
first:
```mojo
from std.ffi import OwnedDLHandle, c_size_t
def main() raises:
var proc = OwnedDLHandle() # No path: opens the current process.
var c_strlen = proc.get_function[c_size_t]("strlen")
# The pointer carries `line`'s origin, so `line` outlives the call.
var line = String("Hello")
var n = c_strlen(line.as_c_string_span())
print(t"length of '{line}': {n}") # 5
# Refill the same variable and call again. The origin still holds.
line = "Hello, Mojo!"
n = c_strlen(line.as_c_string_span())
print(t"length of '{line}': {n}") # 12
```
The pointer's origin ties its lifetime to `line`. Mojo keeps `line` alive
while C uses the pointer. As a result, you don't need workarounds to extend
its lifetime.
## Safety
Inside Mojo, the compiler checks types, tracks lifetimes through origins,
and refuses code that would use a value after it dies. None of that reaches
across the C boundary. C has no origins, no ownership, and no type
information Mojo can read, so the compiler emits exactly the call you
described and trusts you to have described it correctly.
That makes you the type checker. The C header is the contract, and matching
it is your job:
- **Declare what C declares.** Use the `std.ffi` aliases so your types track
the target's C ABI. A mismatch isn't a compile error, it's a wrong answer.
- **Free memory on the side that allocated it.** C memory needs C's
`free()`. Mojo memory has to outlive every C use, including uses that
continue after the call returns.
- **Assume undefined behavior, not exceptions.** A mismatched declaration
usually produces a plausible result rather than a crash, so a passing test
is weak evidence that a declaration is right.
## Unsafe operations
Mojo marks operations it can't check for you with an `unsafe_` prefix, the
same convention used throughout the standard library. This page uses four:
`unsafe_ptr()` to hand C a raw pointer, `unsafe_bitcast()` to reinterpret
one, `unsafe_offset=` to index past the first element, and
`String(unsafe_from_utf8_ptr=)` to trust bytes C gave you.
Each `unsafe_` operation marks a guarantee and responsibility you've taken
over from the compiler.
Origins still help wherever a pointer stays inside Mojo's view. Deriving a
pointer from a variable, as in `line.as_c_string_span()`,
keeps that variable alive for as long as the pointer lives. That protection
ends when C stores the pointer somewhere Mojo can't see.
`external_call()` and `OwnedDLHandle` are intentionally low level. Neither
validates C signatures or protects you from ABI mismatches. Small
declaration mistakes can produce plausible but incorrect results, while
others fail only at build time or when you move to a different platform.
Each entry names the API it applies to.
### Silently wrong at runtime
- **Type matching** (both): Nothing validates your arguments or return type
against the C declaration of the function you're calling.
- **Return type width** (both): Declaring a narrower return than C returns
keeps only the low bits. Declare `strchr`'s `char*` return as `c_int` and a
pointer whose real value is 6199428535 comes back as 1904461239. That's
truncation rather than noise, so a wrong value can still look plausible.
Subtract two truncated pointers and the error cancels, giving you the
right offset for the wrong reason.
- **Argument type width and signedness** (both): Whatever you write becomes
the declaration verbatim. Passing a `c_char` where C declares `int` has the
callee reading a register the caller never fully set.
- **Undeclared argument types** (`OwnedDLHandle`): `get_function()` takes the
return type only, so nothing connects the arguments to the C function's
real signature. Calling `get_function[c_double]("sqrt")` with a `c_int`
returns `0.0` instead of failing.
- **Raw `String` arguments** (`OwnedDLHandle`): `external_call()` rejects a
`String` at compile time, but a callable from `get_function()` accepts one
and reads whatever the struct's bytes happen to be. Passing a 53-byte
`String` to `strlen` returns 5. Always pass
`as_c_string_span()`.
- **Pointers returned into a library** (`OwnedDLHandle`): When a C function
returns a pointer into its own library, the return type must borrow from
the handle, as in `Pointer[c_char, lib_origin]` where
`comptime lib_origin = ImmOrigin(origin_of(lib))`. Declaring
`ImmStaticOrigin` compiles, then reads freed memory once the handle closes
the library.
- **Variadic callees without `num_fixed_args`** (`external_call()`): Pass
`num_fixed_args` for every C variadic function. Without it, each argument
defaults to a fixed argument of a non-variadic callee, which gets the ABI
wrong for `open()` or `snprintf()`. AAPCS on ARM64 macOS passes variadic
arguments differently from fixed ones, so the mistake can work on x86-64
Linux and break on Apple silicon.
- **Platform-varying C types** (both): `c_long` and `c_ulong` resolve per
target rather than to one fixed width. Every platform Mojo supports today
is LP64, so writing `Int64` for a C `long` happens to work. The alias says
what you mean and keeps saying it if the supported targets change.
- **Pointers C keeps after the call** (both): An origin protects a pointer
for as long as Mojo can see it. Mojo can't see C storing your pointer for
later, so a call returning doesn't mean C is finished with what you passed.
Check the C documentation for whether a function retains the pointer.
- **Non-nul-terminated buffers** (both): A bare `Pointer[c_char]` into a
buffer with no terminator sends a C string function reading off the end.
`CStringSpan` is the guardrail for this, ensuring a null terminator is
present.
### Caught at build time
These fail the build, but not always where you'd expect.
- **Two signatures for one symbol in a module** (`external_call()`):
Declaring `strchr` twice with different argument types in the same file
fails to build. The diagnostic points into `std.ffi` rather than at either
of your call sites. This is easy to hit when a wrapper and an inline call
disagree. `get_function()` casts a runtime pointer instead, so it has no
module-level declaration to collide.
- **String arguments** (`external_call()`): Passing a `String` is rejected at
compile time, and the error names `as_c_string_span()` as the fix. This is
the one signature mistake the API catches for you. Take care because the
checking is narrow, and `OwnedDLHandle` doesn't repeat it.
- **Return types must be `RegisterPassable`** (both): `return_type` is bound
to `RegisterPassable`, so the compiler rejects anything larger. A C
function that returns a big struct by value isn't callable directly. C ABIs
return those through a hidden pointer argument, so allocate the struct in
Mojo, pass a pointer to it, and declare the return type as `NoneType`.
### Limitations
- **`external_call()` can't load dynamic libraries**: It calls C functions by
name and leaves the name for Mojo to resolve. Use `OwnedDLHandle` to load a
dynamic library at runtime and retrieve its functions.
- **Function resolution is by C symbol name** (both): C++ functions need
`extern "C"` to be callable.
- **`OwnedDLHandle` resolves everything at runtime**: A wrong library name or
a missing symbol fails when the program runs, not when it builds, and the
library has to be present on the machine that runs the program rather than
the one that built it. `check_symbol()` tests whether a symbol exists and
validates nothing about its signature.
- **`mojo run` and `mojo build` resolve symbols differently**
(`external_call()`): `mojo run` finds the symbol in the already loaded
process image. `mojo build` links through a C compiler driver instead. libc
arrives either way, so a running call is not proof that the same call will
link. A symbol from another system library can resolve under `mojo run` and
then fail under `mojo build` with `DSO missing from command line`. Name the
library in `MODULAR_MOJO_MAX_SYSTEM_LIBS` when that happens.
### What the APIs do check
Two guarantees `OwnedDLHandle` provides that `external_call()` doesn't:
- A missing symbol raises an error rather than aborting the process, so you
can probe for optional symbols.
- The callable from `get_function()` borrows the handle, so the library
can't be closed between the lookup and the call.
## C type reference {#c-type-reference}
{/* markdownlint-disable MD013 */}
| C type | `std.ffi` alias | Equivalent Mojo type | Notes |
|------------------|-----------------|----------------------|-----------------------------------------------------|
| `int` | `c_int` | `Int32` | the most common type by far |
| `short` | `c_short` | `Int16` | |
| `long` | `c_long` | depends on target | 64-bit on Linux and macOS |
| `long long` | `c_long_long` | `Int64` | Always 64 bits. |
| `unsigned char` | `c_uchar` | `UInt8` | |
| `char` | `c_char` | `Int8` | signed; you'll mostly see it as `char*` |
| `unsigned short` | `c_ushort` | `UInt16` | |
| `unsigned int` | `c_uint` | `UInt32` | |
| `unsigned long` | `c_ulong` | depends on target | matches `c_long` |
| `float` | `c_float` | `Float32` | |
| `double` | `c_double` | `Float64` | |
| `size_t` | `c_size_t` | `UInt` | for sizes and counts |
| `ssize_t` | `c_ssize_t` | `Int` | for sizes that can be negative |
| `void*` | `OpaquePointer` | `Pointer[NoneType]` | see the [pointers section](#pointers), uses origins |
{/* markdownlint-enable MD013 */}
---
## Control flow
Mojo includes several traditional control flow structures for conditional and
repeated execution of code blocks.
## The `if` statement
Mojo supports the `if` statement for conditional code execution. With it you can
conditionally execute an indented code block if a given
[boolean](/docs/manual/types/#booleans) expression evaluates to `True`.
```mojo
var temp_celsius = Float64(25)
if temp_celsius > 20:
print("It is warm.")
print("The temperature is", temp_celsius * 9 / 5 + 32, "Fahrenheit." )
```
```output
It is warm.
The temperature is 77.0 Fahrenheit.
```
You can write the entire `if` statement as a single line if all you need to
execute conditionally is a single, short statement.
```mojo
var temp_celsius = 22
if temp_celsius < 15: print("It is cool.") # Skipped because condition is False
if temp_celsius > 20: print("It is warm.")
```
```output
It is warm.
```
Optionally, an `if` statement can include any number of additional `elif`
clauses, each specifying a boolean condition and associated code block to
execute if `True`. The conditions are tested in the order given. When a
condition evaluates to `True`, the associated code block is executed and no
further conditions are tested.
Additionally, an `if` statement can include an optional `else` clause providing
a code block to execute if all conditions evaluate to `False`.
```mojo
var temp_celsius = 25
if temp_celsius <= 0:
print("It is freezing.")
elif temp_celsius < 20:
print("It is cool.")
elif temp_celsius < 30:
print("It is warm.")
else:
print("It is hot.")
```
```output
It is warm.
```
:::note
Mojo doesn't support the equivalent of a Python `match` or C `switch`
statement for pattern matching and conditional execution.
:::
### Short-circuit evaluation
Mojo follows
[short-circuit evaluation](https://en.wikipedia.org/wiki/Short-circuit_evaluation)
semantics for boolean operators. If the first argument to an `or` operator
evaluates to `True`, the second argument is not evaluated.
```mojo
def true_func() -> Bool:
print("Executing true_func")
return True
def false_func() -> Bool:
print("Executing false_func")
return False
print('Short-circuit "or" evaluation')
if true_func() or false_func():
print("True result")
```
```output
Short-circuit "or" evaluation
Executing true_func
True result
```
If the first argument to an `and` operator evaluates to `False`, the second
argument is not evaluated.
```mojo
print('Short-circuit "and" evaluation')
if false_func() and true_func():
print("True result")
```
```output
Short-circuit "and" evaluation
Executing false_func
```
### Conditional expressions
Mojo also supports conditional expressions (or what is sometimes called a
[*ternary conditional operator*](https://en.wikipedia.org/wiki/Ternary_conditional_operator))
using the syntaxtrue_result if boolean_expression
else false_result , just as in Python. This is most often used
as a concise way to assign one of two different values to a variable, based on a
boolean condition.
```mojo
var temp_celsius = 15
var forecast = "warm" if temp_celsius > 20 else "cool"
print("The forecast for today is", forecast)
```
```output
The forecast for today is cool
```
The alternative, written as a multi-line `if` statement, is more verbose.
```mojo
var forecast: String
if temp_celsius > 20:
forecast = "warm"
else:
forecast = "cool"
print("The forecast for today is", forecast)
```
```output
The forecast for today is cool
```
## The `while` statement
The `while` loop repeatedly executes a code block while a given boolean
expression evaluates to `True`. For example, the following loop prints values
from the Fibonacci series that are less than 50.
```mojo
var fib_prev = 0
var fib_curr = 1
print(fib_prev, end="")
while fib_curr < 50:
print(",", fib_curr, end="")
fib_prev, fib_curr = fib_curr, fib_prev + fib_curr
```
```output
0, 1, 1, 2, 3, 5, 8, 13, 21, 34
```
A `continue` statement skips execution of the rest of the code block and
resumes with the loop test expression.
```mojo
var n = 0
while n < 5:
n += 1
if n == 3:
continue
print(n, end=", ")
```
```output
1, 2, 4, 5,
```
A `break` statement terminates execution of the loop.
```mojo
var n = 0
while n < 5:
n += 1
if n == 3:
break
print(n, end=", ")
```
```output
1, 2,
```
Optionally, a `while` loop can include an `else` clause. The body of the `else`
clause executes when the loop's boolean condition evaluates to `False`, even if
it occurs the first time tested.
```mojo
var n = 5
while n < 4:
print(n)
n += 1
else:
print("Loop completed")
```
```output
Loop completed
```
:::note
The `else` clause does *not* execute if a `break` or `return` statement
exits the `while` loop.
:::
```mojo
var n = 0
while n < 5:
n += 1
if n == 3:
break
print(n)
else:
print("Executing else clause")
```
```output
1
2
```
## The `for` statement
The `for` loop iterates over a sequence, executing a code block for each
element in the sequence.
The Mojo `for` loop can iterate over any type that implements an `__iter__()`
method that returns a type that defines `__next__()` and `__len__()` methods.
### Iterating over Mojo collections
All of the collection types in the [`collections`](/docs/std/collections/)
module support `for` loop iteration. See the
[Collection types](/docs/manual/types/#collection-types) documentation for more
information on Mojo collection types.
The following shows an example of iterating over a Mojo
[`List`](/docs/std/collections/list/List/).
```mojo
var states: List[String] = ["California", "Hawaii", "Oregon"]
for state in states:
print(state)
```
```output
California
Hawaii
Oregon
```
The same technique works for iterating over a Mojo
[`Set`](/docs/std/collections/set/Set/).
```mojo
from std.collections import Set
var values = {42, 0}
for item in values:
print(item)
```
```output
42
0
```
There are two techniques for iterating over a Mojo
[`Dict`](/docs/std/collections/dict/Dict/). The first is to iterate directly
using the `Dict`, which produces a sequence of the dictionary's keys.
```mojo
var capitals: Dict[String, String] = {
"California": "Sacramento",
"Hawaii": "Honolulu",
"Oregon": "Salem"
}
for var state in capitals:
print(t"{capitals[state]}, {state}")
```
```output
Sacramento, California
Honolulu, Hawaii
Salem, Oregon
```
The second approach to iterating over a Mojo `Dict` is to invoke its
[`items()`](/docs/std/collections/dict/Dict/#items) method, which produces a
sequence of [`DictEntry`](/docs/std/collections/dict/#dictentry) objects.
Within the loop body, you can then access the `key` and `value` fields of the
entry.
```mojo
for item in capitals.items():
print(t"{item.value}, {item.key}")
```
```output
Sacramento, California
Honolulu, Hawaii
Salem, Oregon
```
#### Iterating using references
The Mojo collection iterators all return
[references](/docs/manual/values/lifetimes/#working-with-references), which
are captured immutably into the loop variable. If you'd like to get a reference
to a mutable element, add the `ref` keyword in front of the loop variable to
create a reference binding that matches the element reference.
This can be useful if you want to mutate the value in the collection:
```mojo
var values: List[Int] = [1, 4, 7, 3, 6, 11]
for ref value in values:
if value % 2 != 0:
value -= 1
print(values)
```
```output
[0, 4, 6, 2, 6, 10]
```
### Iterating ranges
Another type of iterable provided by the Mojo standard library is a *range*,
which is a sequence of integers generated by the
[`range()`](/docs/std/builtin/range/range/) function. It differs from the
collection types shown above in that it's implemented as a
[generator](https://en.wikipedia.org/wiki/Generator_\(computer_programming\)),
producing each value as needed rather than materializing the entire sequence
in memory. For example:
```mojo
for i in range(5):
print(i, end=", ")
```
```output
0, 1, 2, 3, 4,
```
### `for` loop control statements
A `continue` statement skips execution of the rest of the code block and
resumes the loop with the next element of the collection.
```mojo
for i in range(5):
if i == 3:
continue
print(i, end=", ")
```
```output
0, 1, 2, 4,
```
A `break` statement terminates execution of the loop.
```mojo
for i in range(5):
if i == 3:
break
print(i, end=", ")
```
```output
0, 1, 2,
```
Optionally, a `for` loop can include an `else` clause. The body of the `else`
clause executes after iterating over all of the elements in a collection.
```mojo
for i in range(5):
print(i, end=", ")
else:
print("\nFinished executing 'for' loop")
```
```output
0, 1, 2, 3, 4,
Finished executing 'for' loop
```
The `else` clause executes even if the collection is empty.
```mojo
from std.collections import List
var empty: List[Int] = []
for i in empty:
print(i)
else:
print("Finished executing 'for' loop")
```
```output
Finished executing 'for' loop
```
:::note
The `else` clause does *not* execute if a `break` or `return` statement
terminates the `for` loop.
:::
```mojo
from std.collections import List
var animals: List[String] = ["cat", "aardvark", "hippopotamus", "dog"]
for animal in animals:
if animal == "dog":
print("Found a dog")
break
else:
print("No dog found")
```
```output
Found a dog
```
### Iterating over Python collections
The Mojo `for` loop supports iterating over Python collection types. Each item
retrieved by the loop is a
[`PythonObject`](/docs/std/python/python_object/PythonObject/) wrapper around
the Python object. Refer to the [Python types](/docs/manual/python/types/)
documentation for more information on manipulating Python objects from Mojo.
The following is a simple example of iterating over a mixed-type Python list.
```mojo
from std.python import Python
def main() raises:
# Create a mixed-type Python list
var py_list = Python.list(42, "cat", 3.14159)
for py_obj in py_list: # Each element is of type "PythonObject"
print(py_obj)
```
```output
42
cat
3.14159
```
There are two techniques for iterating over a Python dictionary. The first is to
iterate directly using the dictionary, which produces a sequence of its keys.
```mojo
from std.python import Python
def main() raises:
# Create a mixed-type Python dictionary
var py_dict = Python.evaluate("{'a': 1, 'b': 2.71828, 'c': 'sushi'}")
for py_key in py_dict: # Each key is of type "PythonObject"
print(py_key, py_dict[py_key])
```
```output
a 1
b 2.71828
c sushi
```
The second approach to iterating over a Python dictionary is to invoke its
`items()` method, which produces a sequence of 2-tuple objects.
Within the loop body, you can then access the key and value by index.
```mojo
from std.python import Python
def main() raises:
# Create a mixed-type Python dictionary
var py_dict = Python.evaluate("{'a': 1, 'b': 2.71828, 'c': 'sushi'}")
for py_tuple in py_dict.items(): # Each 2-tuple is of type "PythonObject"
print(py_tuple[0], py_tuple[1])
```
```output
a 1
b 2.71828
c sushi
```
---
## Errors, error handling, and context managers
Mojo represents errors as values—specifically, as alternate return values from
functions. Unlike stack-unwinding exceptions in languages like C++ or Java, Mojo
errors don't require expensive call stack unwinding, so their runtime overhead
is as low as returning and checking an extra `Bool`. This design also enables
error handling in contexts where traditional exceptions aren't available, like
GPU kernels.
This page covers:
- [**Raise an error**](#raise-an-error) — Use the built-in `Error` type to
raise errors with string messages.
- [**Handle an error**](#handle-an-error) — Use `try`/`except`/`else`/`finally`
to detect and recover from errors.
- [**Typed errors**](#typed-errors) — Define custom error types as structs for
structured error data and compile-time type checking.
- [**Representing multiple error conditions**](#representing-multiple-error-conditions)
— Use enumerated error types or the `Variant` type for pattern matching.
- [**The `Never` type**](#the-never-type) — Mark functions that always raise
or never raise.
- [**Parametric raises**](#parametric-raises) — Write parameterized functions
that propagate error types from their arguments.
- [**Typed and `Error` interaction**](#typed-errors-and-error-interaction) —
Work with code that uses both error styles.
- [**Stack traces**](#enable-stack-trace-generation-for-errors) — Enable stack
trace collection for debugging.
- [**Context managers**](#use-a-context-manager) — Manage resources safely
with the `with` statement.
An error interrupts the normal execution flow of your program. If you provide an
error handler (using [`try`/`except`](#handle-an-error)) in the current
function, execution resumes with that handler. If the error isn't handled in the
current function, it propagates to the calling function, and so on. If an error
isn't caught by any handler, your program terminates with a non-zero exit code
and prints the error message:
```output
Unhandled exception caught during execution: record not found
```
## Raise an error
The built-in [`Error`](/docs/std/builtin/error/Error/) type is the default error
type for most Mojo code. It carries a text message describing what went wrong,
and it's the right choice for application-level error handling—simple,
well-supported, and sufficient for the majority of use cases.
You can raise an `Error` with the initializer or a string literal shorthand:
```mojo
# These are equivalent
raise Error("file not found")
raise "file not found"
```
The string literal form is a convenience—the compiler automatically wraps it in
an `Error`.
By declaring `raises`, you tell Mojo that a function may raise an error:
```mojo
def read_file_fn(path: String) raises -> String:
if not path:
raise "path cannot be empty"
return "contents of " + path
```
:::tip
If you need structured error data—like separate fields for an error code and
description—or allocation-free errors for GPU kernels, see
[Typed errors](#typed-errors) later on this page. For most application code,
`Error` with a descriptive string message is all you need.
:::
## Handle an error
Mojo uses `try`/`except` to detect and handle errors. The full syntax is:
```mojo
try:
# Code that might raise an error
except e:
# Runs if an error occurs
else:
# Runs if no error occurs
finally:
# Always runs, regardless of outcome
```
You must include one or both of `except` and `finally`. The `else` clause is
optional.
### How each clause works
- `try` — Contains code that might raise an error. If no error occurs, the
entire block executes. If an error occurs, execution stops at the `raise`
point and continues with the `except` clause (if present) or the `finally`
clause.
- `except` — Runs only when an error occurs in the `try` block. If you
provide a variable name (`except e:`), the error is bound to that variable.
A `try` block can have only one `except` clause.
- `else` — Runs only when no error occurs in the `try` block. The `else`
clause is *skipped* if the `try` clause exits via `continue`, `break`, or
`return`.
- `finally` — Runs after the `try` and any `except` or `else` clause,
regardless of outcome. It executes even if another clause exits via
`continue`, `break`, `return`, or by raising a new error. Use `finally` to
release resources (such as file handles) that must be cleaned up regardless
of whether an error occurred.
### Example
The following example demonstrates all four clauses. The `process_record()`
function raises `Error` for different conditions, and the caller loops over
a list of IDs to exercise each clause:
```mojo title="handle_error.mojo"
def process_record(id: Int) raises -> String:
if id < 0:
raise Error("invalid record ID: must be non-negative")
if id > 999:
raise Error("record not found")
return String("record_", id)
def main() raises:
try:
for id in [5, 0, 1001, -3, 42]:
var result: String
try:
print()
print("try => id:", id)
if id == 0:
continue
result = process_record(id)
except e:
if "invalid" in String(e):
print("except => fatal:", e)
raise e
print("except => handled:", e)
else:
print("else => success:", result)
finally:
print("finally => done with id:", id)
except e:
print("\nre-raised error:", e)
```
```output
try => id: 5
else => success: record_5
finally => done with id: 5
try => id: 0
finally => done with id: 0
try => id: 1001
except => handled: record not found
finally => done with id: 1001
try => id: -3
except => fatal: invalid record ID: must be non-negative
finally => done with id: -3
re-raised error: invalid record ID: must be non-negative
```
Notice:
- When `id` is 5: `process_record()` succeeds, so `else` runs, then `finally`.
- When `id` is 0: `continue` exits the `try` block, skipping both `except`
and `else`. Only `finally` runs.
- When `id` is 1001: `process_record()` raises an error. The `except` clause
handles it and execution continues with the next iteration.
- When `id` is -3: `process_record()` raises an "invalid" error. The `except`
clause re-raises it, so it propagates to the outer `try`/`except`. The
`finally` clause still runs before the error propagates. Because the re-raise
exits the loop, `id` 42 is never processed.
### Re-raise an error
To re-raise a caught error, pass it to `raise`:
```mojo
try:
var result = process_record(-1)
except e:
print("Logging error:", e)
raise e # re-raise
```
You can also raise a different error from within an `except` clause.
Re-raising copies the error, which is cheap because both its message and its
optional stack trace are reference counted. To avoid the copy, transfer the
error with the
[transfer sigil](/docs/manual/values/ownership/#transfer-arguments-var-and-):
`raise e^`. A [custom error type](#typed-errors) that doesn't conform to
[`ImplicitlyCopyable`](/docs/std/traits/copyable/ImplicitlyCopyable/) requires
the transfer sigil to re-raise.
## Typed errors
For code that needs more than a string message—like standard library APIs,
GPU abstractions, or situations where callers need structured error data—Mojo
lets you define custom error types as [structs](/docs/manual/structs/).
### Define a custom error type
In Mojo, any struct can serve as an error type—no special base class or trait
is required. However, implementing the
[`Writable`](/docs/std/format/Writable/) trait is recommended so the error
produces a readable message when printed or when the program terminates with an
unhandled error:
```mojo
@fieldwise_init
struct ValidationError(Copyable, Writable):
var field: String
var reason: String
def write_to(self, mut writer: Some[Writer]):
writer.write("ValidationError(", self.field, "): ", self.reason)
```
The [`@fieldwise_init`](/docs/reference/decorators/fieldwise-init/) decorator
generates an `__init__()` method with an argument for each field, so you
can construct errors like `ValidationError("username", "too short")` or with
keyword arguments like `ValidationError(field="username", reason="too short")`.
:::note
Typed errors work on GPUs and embedded targets as long as you avoid
heap-allocated types like `String`. For more on GPU programming, see
[GPU fundamentals](https://max.modular.com/gpu/fundamentals/).
:::
### Raise a typed error
To declare that a function can raise a typed error, add `raises YourErrorType`
to its signature:
```mojo
def validate_username(username: String) raises ValidationError -> String:
if username.byte_length() == 0:
raise ValidationError(field="username", reason="cannot be empty")
if username.count_codepoints() < 3:
raise ValidationError(
field="username", reason="must be at least 3 characters"
)
return username
```
Mojo functions are non-raising by default. Including `raises` (with or
without a type) makes it a raising function. Each function can declare
at most one error type. The compiler enforces this—if any `raise`
statement in the function body doesn't match the declared type, the
program won't compile.
If a non-raising function calls a raising function, it must handle the error
locally:
```mojo
# This doesn't compile — validate_username() can raise
def process_name(name: String):
print(validate_username(name))
# This compiles — the error is handled
def process_name_safe(name: String):
try:
print(validate_username(name))
except e:
print("Invalid:", e)
```
### Catch a typed error
Use `try`/`except` to catch a typed error. The compiler automatically infers the
error type from the function being called, so `except e:` gives you a fully
typed error value—no casting required:
```mojo
try:
var name = validate_username("")
except e:
# e is a ValidationError — access fields directly
print("Error in field '" + e.field + "': " + e.reason)
```
Which produces this output:
```output
Error in field 'username': cannot be empty
```
A `try` block can include only one `except` clause. Mojo doesn't support
`except ErrorType as e:` syntax—the type is always inferred from the
function being called.
If you need to handle calls that raise different error types, use separate
`try` blocks:
```mojo
# Each try block handles one error type
try:
var name = validate_username(input)
except e:
# e is a ValidationError — access fields directly
print("Validation failed:", e.field, e.reason)
try:
var file = open_file(path)
except e:
# e is a FileError — match on variant
if e == FileError.not_found:
print("Missing:", path)
```
:::note
If your error type implements the [`Writable`](/docs/std/format/Writable/)
trait, you can also pass `e` directly to `print()`:
```mojo
except e:
print(e) # calls ValidationError.write_to()
```
Which produces this output:
```output
ValidationError(username): cannot be empty
```
:::
## Representing multiple error conditions
Each function can declare only one error type in its `raises` clause. When
a function can fail in multiple distinct ways, you need to represent those
conditions within a single type. Mojo offers two approaches:
- **Enumerated error types** — A single struct with `comptime` variant aliases.
Simpler and more efficient when you only need to distinguish between
conditions.
- **The `Variant` type** — The standard library
[`Variant`](/docs/std/utils/variant/Variant/) type with separate structs per
condition. More flexible when each condition needs to carry different data.
### Enumerated error types
A single struct can represent all error conditions using an integer `_variant`
field and
[`comptime` values](/docs/manual/metaprogramming/comptime-evaluation/#comptime-values)
as named constants. The `write_to` method generates human-readable strings from
the variant code:
```mojo
@fieldwise_init
struct FileError(Equatable, ImplicitlyCopyable, Writable):
var _variant: Int
# Compile-time constant variants
comptime not_found = FileError(_variant=1)
comptime permission_denied = FileError(_variant=2)
comptime already_exists = FileError(_variant=3)
def variant_name(self) -> String:
if self._variant == 1:
return "not_found"
elif self._variant == 2:
return "permission_denied"
elif self._variant == 3:
return "already_exists"
return "unknown"
def write_to(self, mut writer: Some[Writer]):
writer.write("FileError.", self.variant_name())
```
Because `FileError` has a single `Int` field and conforms to
[`Equatable`](/docs/std/builtin/comparable/Equatable/), the compiler
auto-synthesizes `__eq__()`, so you can compare variants directly:
```mojo
def open_file(path: String) raises FileError -> String:
if not path:
raise FileError.not_found
if path == "/secret":
raise FileError.permission_denied
return "Contents of " + path
```
You can then match on specific variants in the handler:
```mojo
try:
print(open_file("/secret"))
except e:
if e == FileError.not_found:
print("Not found:", e)
elif e == FileError.permission_denied:
print("Permission denied:", e)
```
Which produces this output:
```output
Permission denied: FileError.permission_denied
```
### The `Variant` type
When each error condition needs to carry different data, you can use the
standard library [`Variant`](/docs/std/utils/variant/Variant/) type instead of
an integer-based enumeration. Define a separate struct for each condition, then
combine them into a single error type with a `comptime` alias:
```mojo title="variant_errors.mojo"
from std.utils import Variant
@fieldwise_init
struct NotFoundError(Copyable, Writable):
var path: String
def write_to(self, mut writer: Some[Writer]):
writer.write("file not found: ", self.path)
@fieldwise_init
struct PermissionError(Copyable, Writable):
var path: String
var required_role: String
def write_to(self, mut writer: Some[Writer]):
writer.write(
"permission denied on ",
self.path,
" (requires ",
self.required_role,
")",
)
comptime FileError = Variant[NotFoundError, PermissionError]
```
Construct a `Variant` by wrapping the inner error in the `Variant` type:
```mojo
def open_file(path: String) raises FileError -> String:
if not path:
raise FileError(NotFoundError(""))
if path == "/secret":
raise FileError(PermissionError("/secret", "admin"))
return "Contents of " + path
```
In the handler, use `.isa[T]()` to test which condition occurred and `e[T]` to
access the inner error with its full type:
```mojo
try:
print(open_file("/secret"))
except e:
if e.isa[NotFoundError]():
print("Not found:", e[NotFoundError])
elif e.isa[PermissionError]():
print("Access denied:", e[PermissionError])
```
```output
Access denied: permission denied on /secret (requires admin)
```
Use the `Variant` approach when each condition carries different fields (like
`path` vs `path` + `required_role` above). Use the
[enumerated error type](#enumerated-error-types) pattern when you only need to
distinguish between conditions without carrying different data per condition.
## The `Never` type
`Never` is a type with no initializers. It can't be instantiated. This makes
it useful in error-handling signatures to express two opposite guarantees:
- `raises YourErrorType -> Never` — The function *always* raises and never
returns a value. This is useful for functions like `panic()` that
unconditionally signal an error.
- `raises Never -> ReturnType` — The function *never* raises and always returns
a value. This is equivalent to omitting `raises` entirely.
### Functions that always raise
A function with `-> Never` as its return type must never terminate with a
`return` statement—it must raise on every code path (or loop infinitely).
Because `Never` can substitute for any type, the compiler allows using such a
function in place of a return value:
```mojo
# Always raises, never returns
def panic(msg: String) raises -> Never:
raise Error(msg)
def get_value_or_panic(maybe: Optional[Int]) raises -> Int:
if maybe:
return maybe.value()
# Never substitutes for Int in this branch
panic("value is missing")
```
### Functions that never raise
A function with `raises Never` guarantees at compile time that it never raises.
This is equivalent to writing a plain non-raising function:
```mojo
# These two signatures are equivalent:
def safe_add(a: Int, b: Int) raises Never -> Int:
return a + b
def safe_add(a: Int, b: Int) -> Int:
return a + b
```
This equivalency is especially useful in combination with
[parametric raises](#parametric-raises), where the compiler infers
`raises Never` when a function argument doesn't raise.
## Parametric raises
You can write
[parameterized functions](/docs/manual/parameters/#parameters-and-generics) that
propagate the error type from a function argument to the caller. This uses a
compile-time parameter for the error type:
```mojo
def run_action[
ErrorType: AnyType
](action: def() thin raises ErrorType -> Int) raises ErrorType -> Int:
return action()
```
The function type uses `thin` because `action` is a noncapturing function
value.
The `ErrorType` parameter is inferred from the function you pass in. If the
function raises `NetworkError`, then `run_action` raises `NetworkError`. If the
function raises `ParseError`, then `run_action` raises `ParseError`:
```mojo
def fetch_data() raises NetworkError -> Int:
raise NetworkError(code=404)
def parse_config() raises ParseError -> Int:
raise ParseError(position=42)
# ...
# ErrorType inferred as NetworkError
try:
_ = run_action(fetch_data)
except e:
print("Network failure:", e)
# ErrorType inferred as ParseError
try:
_ = run_action(parse_config)
except e:
print("Parse failure:", e)
```
If the function argument doesn't raise at all, the compiler infers `Never` as
the error type. This means `run_action` itself becomes non-raising, and no `try`
block is needed:
```mojo
def get_value() -> Int:
return 99
# ...
# ErrorType inferred as Never — no try block needed
var result = run_action(get_value)
print("Got value:", result)
```
Which produces this output:
```output
Got value: 99
```
## Typed errors and `Error` interaction
Most codebases contain a mix of functions that raise, don't raise, or
raise typed errors. This section covers how the styles interact and how
to work with them effectively.
### Wrap `Error` at API boundaries
When calling raising functions or other `Error`-raising code from a function
that uses typed errors, catch the `Error` and convert it:
```mojo
def validate_with_error(value: Int) raises -> Int:
if value < 0:
raise "value cannot be negative"
return value
def wrapped_validate(value: Int) raises ValidationError -> Int:
try:
return validate_with_error(value)
except e:
raise ValidationError(field="value", reason=String(e))
```
### Avoid bare `raises` with typed errors
Using bare `raises` (without a type) on an function that calls typed-error
functions causes *type erasure*—the compiler forgets the specific error type,
even though the runtime preserves the error's identity:
```mojo
# Anti-pattern: bare raises erases type info at compile time
def validate_bare_raises(value: Int) raises -> Int:
return validate_typed(value)
```
The caller of `validate_bare_raises()` receives an `Error`, not a
`ValidationError`:
```mojo
try:
_ = validate_bare_raises(-5)
except e:
# e is typed as Error — no field access available
# e.field would not compile here
print(e)
```
```output
ValidationError(value): cannot be negative
```
The error message still shows `ValidationError` because the runtime preserves
the original error's [`Writable`](/docs/std/format/Writable/) output. But the
compiler sees only `Error`, so you lose access to structured fields. Always use
`raises YourErrorType` to maintain type safety.
Note that type erasure only affects *uncaught* errors that propagate through a
bare `raises` function. If you catch the typed error locally, you still get full
field access:
```mojo
def error_caller():
try:
_ = validate_typed(-5)
except e:
# e is a ValidationError — field access works
print("Field:", e.field, "Reason:", e.reason)
```
```output
Field: value Reason: cannot be negative
```
### Don't mix error types in a single `try` block
You can't call functions that raise different error types in the same `try`
block. The compiler rejects the mismatch:
```mojo
def error_func() raises -> Int:
raise "something went wrong"
def typed_func() raises ValidationError -> Int:
raise ValidationError(field="x", reason="invalid")
# This doesn't compile
def mixed() raises ValidationError:
try:
_ = error_func() # raises Error
_ = typed_func() # raises ValidationError
except e:
print(e)
```
The compiler reports:
```output
error: cannot call function that may raise 'Error' in a context that
supports an error type of 'ValidationError'
```
To call both functions, use separate `try` blocks or wrap the `Error`-raising
function as shown in
[Wrap `Error` at API boundaries](#wrap-error-at-api-boundaries).
### Recommendations for mixed codebases
When working with both `Error` and typed errors:
- **Use `raises YourErrorType`** — Always specify the error type in function
signatures. Bare `raises` discards type information.
- **Use separate `try` blocks** — When calling functions with different error
types, use nested or sequential `try` blocks to handle each type
independently.
:::note
For a complete working example of these interaction patterns, see
[`error_interaction.mojo`](https://github.com/modular/modular/blob/mojo/v1.1.0/Mojo/docs/site/code/manual/errors/error_interaction.mojo).
:::
## Enable stack trace generation for errors
Because Mojo represents errors as alternate return values rather than
stack-unwinding exceptions, stack trace collection isn't automatic. Collecting
a stack trace requires heap allocation and adds runtime overhead, so it's
disabled by default to keep error handling lightweight.
:::important
Stack traces are a feature of the built-in `Error` type only. Typed errors
currently don't capture stack traces because the trace is collected inside
`Error.__init__()`, and custom error structs have no equivalent hook. The
examples in this section all use `Error` intentionally.
:::
Mojo generates a stack trace when your program hits a segmentation fault.
However, by default Mojo *doesn't* generate a stack trace when your program
raises an error—this avoids the additional runtime overhead. To enable stack
traces for raised errors, set the `MODULAR_DEBUG` environment variable to
`stack-trace-on-error`, as shown in the examples below.
Keep in mind that when you compile your program with
[`mojo build`](/docs/cli/build/), the compiler optimizes and strips symbols by
default, so often your stack trace won't be very useful.
Consider this program:
```mojo title="stacktrace_error.mojo"
def func2() raises -> None:
raise Error("Intentional error")
def func1() raises -> None:
func2()
def main() raises:
func1()
```
If you compile the program with default settings and run it with the
environment variable set, you'll see a stack trace without symbols:
```sh
mojo build stacktrace_error.mojo
```
```sh
MODULAR_DEBUG=stack-trace-on-error ./stacktrace_error
```
```output
#0 0x... llvm::sys::PrintStackTrace(llvm::raw_ostream&, int)
#1 0x... KGEN_CompilerRT_GetStackTrace
#2 0x... main (./stacktrace_error+...)
Unhandled exception caught during execution: Intentional error
```
To generate a more useful stack trace, compile the program with
`--debug-level full` (or `-g`) to include debug symbols:
```sh
mojo build --debug-level full stacktrace_error.mojo
```
```sh
MODULAR_DEBUG=stack-trace-on-error ./stacktrace_error
```
```output
#0 0x... llvm::sys::PrintStackTrace(llvm::raw_ostream&, int)
#1 0x... KGEN_CompilerRT_GetStackTrace
#2 0x... Error.__init__[...](...) .../builtin/error.mojo:159:38
#3 0x... stacktrace_error::func2() stacktrace_error.mojo:14:16
#4 0x... stacktrace_error::func1() stacktrace_error.mojo:18:10
#5 0x... stacktrace_error::main() stacktrace_error.mojo:22:10
#6 0x... __wrap_and_execute_raising_main[...](...) .../builtin/_startup.mojo:88:18
#7 0x... main .../builtin/_startup.mojo:103:4
Unhandled exception caught during execution: Intentional error
```
With debug symbols, the trace shows the function call chain and source
locations: `main()` → `func1()` → `func2()` → `Error.__init__()`.
:::note
Running your program directly with [`mojo run`](/docs/cli/run/) or
`mojo` doesn't include debug symbols in the stack trace, even with
`--debug-level full`. Use `mojo build` with `-g` and run the compiled binary
for symbolicated stack traces.
:::
### Capture a stack trace programmatically
You can bind the `Error` instance to a variable in the `except` clause and
call its
[`get_stack_trace()`](/docs/std/builtin/error/Error/#get_stack_trace) method
to get the stack trace as an `Optional[String]`. The method returns `None` if
stack trace collection was disabled or unavailable:
```mojo title="stacktrace_error_capture.mojo"
def func2() raises -> None:
raise Error("Intentional error")
def func1() raises -> None:
func2()
def main() raises:
try:
func1()
except e:
print(e)
print("-" * 20)
var stack_trace = e.get_stack_trace()
if stack_trace:
print(stack_trace.value())
else:
print("No stack trace available")
```
When you compile with debug symbols and run with stack trace generation enabled:
```sh
mojo build --debug-level full stacktrace_error_capture.mojo
```
```sh
MODULAR_DEBUG=stack-trace-on-error ./stacktrace_error_capture
```
```output
Intentional error
--------------------
#0 0x... llvm::sys::PrintStackTrace(llvm::raw_ostream&, int)
#1 0x... KGEN_CompilerRT_GetStackTrace
#2 0x... Error.__init__[...](...) .../builtin/error.mojo:159:38
#3 0x... stacktrace_error_capture::func2() stacktrace_error_capture.mojo:14:16
#4 0x... stacktrace_error_capture::func1() stacktrace_error_capture.mojo:18:10
#5 0x... stacktrace_error_capture::main() stacktrace_error_capture.mojo:23:14
#6 0x... __wrap_and_execute_raising_main[...](...) .../builtin/_startup.mojo:88:18
#7 0x... main .../builtin/_startup.mojo:103:4
```
Without enabling stack trace generation, the output is:
```output
Intentional error
--------------------
No stack trace available
```
## Use a context manager
A *context manager* is an object that manages resources such as files, network
connections, and database connections. It provides a way to allocate resources
and release them automatically when they are no longer needed, ensuring proper
cleanup and preventing resource leaks even when errors occur.
:::note
Context managers work with both typed errors and the built-in `Error` type. The
`with` statement handles either error style transparently.
:::
As an example, consider reading data from a file. A naive approach might look
like this:
```mojo
# Obtain a file handle to read from storage
var f = open(input_file, "r")
var content = f.read()
# Process the content as needed
# Close the file handle
f.close()
```
Calling [`close()`](/docs/std/io/file/FileHandle/#close) releases the
memory and other operating system resources associated with the opened file. If
your program were to open many files without closing them, you could exhaust the
resources available to your program and cause errors. The problem is even worse
if you were writing to a file instead of reading from it, because the operating
system might buffer the output in memory until the file is closed. If your
program were to crash instead of exiting normally, that buffered data could be
lost instead of being written to storage.
The example above includes the call to `close()`, but it ignores the
possibility that [`read()`](/docs/std/io/file/FileHandle/#read) could
raise an error, which would prevent the `close()` from executing.
To handle this scenario, you could rewrite the code to use `try` like this:
```mojo
# Obtain a file handle to read from storage
var f = open(input_file, "r")
try:
var content = f.read()
# Process the content as needed
finally:
# Ensure that the file handle is closed even if read() raises an error
f.close()
```
However, the [`FileHandle`](/docs/std/io/file/FileHandle/) struct
returned by [`open()`](/docs/std/io/file/open/) is a context manager.
When used with Mojo's `with` statement, a context manager ensures that the
resources it manages are properly released at the end of the block, even if an
error occurs. In the case of a `FileHandle`, that means the call to `close()`
takes place automatically. So you could rewrite the example above to take
advantage of the context manager (and omit the explicit call to `close()`)
like this:
```mojo
with open(input_file, "r") as f:
var content = f.read()
# Process the content as needed
```
The `with` statement also allows you to use multiple context managers within the
same code block. As an example, the following code opens one text file, reads
its entire content, converts it to upper case, and then writes the result to a
different file:
```mojo
with open(input_file, "r") as f_in, open(output_file, "w") as f_out:
var input_text = f_in.read()
var output_text = input_text.upper()
f_out.write(output_text)
```
`FileHandle` is perhaps the most commonly used context manager. Other examples
of context managers in the Mojo standard library are
[`NamedTemporaryFile`](/docs/std/tempfile/tempfile/NamedTemporaryFile/),
[`TemporaryDirectory`](/docs/std/tempfile/tempfile/TemporaryDirectory/),
[`BlockingScopedLock`](/docs/std/utils/lock/BlockingScopedLock/), and
[`assert_raises`](/docs/std/testing/testing/assert_raises/). You can also
create your own custom context managers, as described in [Write a custom context
manager](#write-a-custom-context-manager) below.
## Write a custom context manager
Writing a custom context manager is a matter of defining a
[struct](/docs/manual/structs/) that implements two special *dunder* methods
("double underscore" methods): `__enter__()` and `__exit__()`:
- `__enter__()` is called by the `with` statement to enter the runtime context.
The `__enter__()` method should initialize any state necessary for the context
and return the context manager.
- `__exit__()` is called when the `with` code block completes execution, even if
the `with` code block terminates with a call to `continue`, `break`, or
`return`. The `__exit__()` method should release any resources associated with
the context. After the `__exit__()` method returns, the context manager is
destroyed.
If the `with` code block raises an error, then the `__exit__()` method runs
before any error processing occurs (that is, before it is caught by a
`try`/`except` structure or your program terminates). If you'd like to define
conditional processing for error conditions in a `with` code block, you can
implement an overloaded version of `__exit__()` that takes an error
argument. For more information, see
[Define a conditional `__exit__()` method](#define-a-conditional-__exit__-method)
and
[Handle typed errors in `__exit__()`](#handle-typed-errors-in-__exit__)
below.
For context managers that don't need to release resources or perform other
actions on termination, you are not required to implement an `__exit__()`
method. In that case the context manager is destroyed automatically after the
`with` code block completes execution.
Here is an example of implementing a `Timer` context manager, which prints the
amount of time spent executing the `with` code block:
```mojo title="context_mgr.mojo"
import std.sys
import std.time
@fieldwise_init
struct Timer(ImplicitlyCopyable):
var start_time: Int
def __init__(out self):
self.start_time = 0
def __enter__(mut self) -> Self:
self.start_time = Int(time.perf_counter_ns())
return self
def __exit__(mut self):
var end_time = time.perf_counter_ns()
var elapsed_time_ms = round(
Float64(end_time - self.start_time) / 1e6, 3
)
print("Elapsed time:", elapsed_time_ms, "milliseconds")
def main() raises:
with Timer():
print("Beginning execution")
time.sleep(1.0)
if len(sys.argv()) > 1:
raise "simulated error"
time.sleep(1.0)
print("Ending execution")
```
Running this example produces output like this:
```sh
mojo context_mgr.mojo
```
```output
Beginning execution
Ending execution
Elapsed time: 2010.0 milliseconds
```
```sh
mojo context_mgr.mojo fail
```
```output
Beginning execution
Elapsed time: 1002.0 milliseconds
Unhandled exception caught during execution: simulated error
```
### Define a conditional `__exit__()` method
When creating a context manager, you can implement the `__exit__(self)` form of
the `__exit__()` method to handle completion of the `with` statement under all
circumstances including errors. However, you have the option of additionally
implementing an overloaded version that is invoked instead when an `Error`
occurs in the `with` code block:
```mojo
def __exit__(self, error: Error) raises -> Bool
```
Given the `Error` that occurred as an argument, the method can do any of the
following:
- Return `True` to suppress the error.
- Return `False` to re-raise the error.
- Raise a new error.
The following is an example of a context manager that suppresses only a certain
error condition and propagates all others:
```mojo title="conditional_context_mgr.mojo"
import std.time
@fieldwise_init
struct ConditionalTimer(ImplicitlyCopyable):
var start_time: Int
def __init__(out self):
self.start_time = 0
def __enter__(mut self) -> Self:
self.start_time = Int(time.perf_counter_ns())
return self
def __exit__(mut self):
var end_time = time.perf_counter_ns()
var elapsed_time_ms = round(
Float64(end_time - self.start_time) / 1e6, 3
)
print("Elapsed time:", elapsed_time_ms, "milliseconds")
def __exit__(mut self, e: Error) -> Bool:
if String(e) == "just a warning":
print("Suppressing error:", e)
self.__exit__()
return True
else:
print("Propagating error")
self.__exit__()
return False
def flaky_identity(n: Int) raises -> Int:
if (n % 4) == 0:
raise "really bad"
elif (n % 2) == 0:
raise "just a warning"
else:
return n
def main() raises:
for i in range(1, 9):
with ConditionalTimer():
print("\nBeginning execution")
print("i =", i)
time.sleep(0.1)
if i == 3:
print("continue executed")
continue
var j = flaky_identity(i)
print("j =", j)
print("Ending execution")
```
Running this example produces this output:
```output
Beginning execution
i = 1
j = 1
Ending execution
Elapsed time: 105.0 milliseconds
Beginning execution
i = 2
Suppressing error: just a warning
Elapsed time: 106.0 milliseconds
Beginning execution
i = 3
continue executed
Elapsed time: 106.0 milliseconds
Beginning execution
i = 4
Propagating error
Elapsed time: 106.0 milliseconds
Unhandled exception caught during execution: really bad
```
### Handle typed errors in `__exit__()`
The `__exit__(self, error: Error)` overload handles only `Error` values.
To handle typed errors, implement a parameterized `__exit__()` method with a
compile-time error type parameter:
```mojo
def __exit__[ErrType: AnyType](self, err: ErrType) -> Bool
```
This method receives the typed error directly, preserving its full type
information. You can use
[reflection](/docs/manual/metaprogramming/reflection/) to inspect the error
type at compile time. For example, you can:
- `reflect[ErrType].name()` — Get the error type's name as a string.
- `comptime if conforms_to(ErrType, Writable)` — Check if the error
implements `Writable`, and if so, access the error through its `Writable`
interface.
The following `ResourceGuard` example demonstrates this pattern:
```mojo
from std.reflection import *
@fieldwise_init
struct ConnectionError(Copyable, Writable):
var message: String
def write_to(self, mut writer: Some[Writer]):
writer.write("ConnectionError: ", self.message)
struct ResourceGuard(ImplicitlyCopyable):
var name: String
var suppress_errors: Bool
def __init__(out self, name: String, suppress_errors: Bool = False):
self.name = name
self.suppress_errors = suppress_errors
def __enter__(self) -> Self:
print("Acquiring:", self.name)
return self
def __exit__(self):
print("Releasing:", self.name, "(no error)")
def __exit__[ErrType: AnyType](self, err: ErrType) -> Bool:
comptime type_name = reflect[ErrType].name()
print("Releasing:", self.name)
print(" Error type:", type_name)
comptime if conforms_to(ErrType, Writable):
print(" Message:", err)
return self.suppress_errors
```
When no error occurs, `__exit__(self)` runs as usual. When a typed error occurs,
`__exit__[ErrType]()` runs instead, giving you access to the error type and
its data:
```mojo
# No error — calls __exit__(self)
with ResourceGuard("database"):
print("Working...")
# Typed error, suppressed — __exit__[ErrType] returns True
with ResourceGuard("cache", suppress_errors=True):
use_connection() # raises ConnectionError
print("Continued after suppressed error")
```
```output
Acquiring: database
Working...
Releasing: database (no error)
Acquiring: cache
Releasing: cache
Error type: ConnectionError
Message: ConnectionError: connection timed out
Continued after suppressed error
```
:::note
For a complete working example including error suppression and propagation, see
[`resource_guard.mojo`](https://github.com/modular/modular/blob/mojo/v1.1.0/Mojo/docs/site/code/manual/errors/resource_guard.mojo).
:::
---
## Closures
:::caution Evolving feature
Closures are a longstanding part of Mojo, used throughout the standard
library and kernel infrastructure.
Mojo's updated capture-list syntax is now available. Code examples
on this page reflect the redesigned compiler behavior.
:::
A *closure* is a function bundled together with values from its surrounding
scope. You define it in one place and pass it somewhere else to run. The
closure carries captured data with it. The compiler transforms it to a type
with both behavior and storage. The code executing the closure doesn't
need to know where that data came from.
This allows closures to carry state and configuration into code that
executes later or elsewhere. Configuration uses capture conventions to
specify how the closure interacts with captured values, choosing between
read-only references, mutable references, copies, or moves.
Mojo closures look like nested functions, but they use a special
syntax. Curly braces after the argument list form a *capture list* that
declares which outer values the closure captures and how it interacts
with them.
The capture list is what distinguishes a closure from an ordinary nested
function. It gives the compiler the information needed to manage captured
values safely and eliminate ambiguity. When you specify how a value is
captured, the compiler can enforce correct usage:
```mojo
def main():
var multiplier = 3
def scale(x: Int) {imm multiplier} -> Int:
return x * multiplier
print(scale(5)) # 15
```
`scale` is a closure. It captures `multiplier` from the enclosing
scope using its capture list (`{imm multiplier}`). When you call
`scale(5)`, the closure multiplies `5` by the captured value of
`multiplier` and returns `15`.
Without a capture list, the inner function can't see anything outside its
own arguments.
## Why closures matter
Closures package behavior together with the data that behavior
needs. You define the work in one place, then pass that package for
execution later, elsewhere, or on different hardware.
This separation between defining work and executing work is central
to how Mojo expresses computation.
## The capture list
A capture list such as `{imm x, mut y}` tells the compiler how the
closure captures and uses values from the surrounding scope.
For this list, `x` is captured as an immutable reference. The closure
can read it but can't modify it. `y` is captured as a mutable
reference, so the closure can modify it and those changes are visible
in the outer scope.
Mojo requires captures to be explicit. In a systems language, knowing
exactly which values a closure holds, and whether it reads, copies, or
takes ownership of them, matters for both performance and correctness.
## Capture by immutable reference: `imm`
Use `imm` when the closure needs to see a value but not change it:
```mojo
def main():
var threshold = 100
def is_over(x: Int) {imm threshold} -> Bool:
return x > threshold
print(is_over(50)) # False
print(is_over(200)) # True
```
The closure reads an immutable reference to `threshold`. It sees the
current value each time it's called, including changes made after the
closure was created:
```mojo
def main():
var limit = 10
def check(x: Int) {imm limit} -> Bool:
return x < limit
print(check(5)) # True
limit = 3
print(check(5)) # False (sees updated limit)
```
Because `imm` captures a reference, the closure reflects the
live state of the original value.
To capture every used outer value by immutable reference without
naming each one, use `{imm}`:
```mojo
def main():
var a = 1
var b = 2
def sum_ab() {imm} -> Int:
return a + b
print(sum_ab()) # 3
```
## Capture by mutable reference: `mut`
Use `mut` when a closure needs to modify a captured value and make
those changes visible in the enclosing scope:
```mojo
def main():
var total = 0
def accumulate(x: Int) {mut total}:
total += x
accumulate(10)
accumulate(20)
print(total) # 30
```
Changes to `total` inside the closure modify the original variable
directly. This is a mutable reference, not a copy.
The implicit form `{mut}` captures every used outer value with a
mutable reference:
```mojo
def main():
var count = 0
var items = List[String]()
def record(name: String) {mut}:
items.append(name)
count += 1
record("alpha")
record("beta")
print(count) # 2
print(items) # ['alpha', 'beta']
```
## Capture by copy: `var`
Use `var` when the closure needs its own independent copy of a
value. Changes to the original don't affect the closure, and
changes inside the closure don't affect the original.
```mojo
def main():
var snapshot_val = 42
def frozen() {var snapshot_val} -> Int:
return snapshot_val
snapshot_val = 999
print(frozen()) # 42 (captured the value at definition time)
```
The closure copied `snapshot_val` when it was created. Later
changes to `snapshot_val` in the outer scope don't affect the
closure's copy.
The implicit form `{var}` copies every used outer value:
```mojo
def main():
var x = 10
var y = 20
def snap() {var} -> Int:
return x + y
x = 0
y = 0
print(snap()) # 30 (uses copied values)
```
:::note
Copy captures call the value's copy initializer at the point
where the closure is defined. For types with expensive copies
(large lists, strings with allocations), prefer `imm` or `mut`
when you don't need an independent copy.
:::
## Move capture: `var name^`
Use `var name^` to transfer ownership of a value into the closure.
The closure consumes the outer binding, which can't be used after
the closure is created:
```mojo
def main():
var data: List[Int] = [1, 2, 3]
def take_data() {var data^}:
print(data)
take_data() # [1, 2, 3]
# data can't be used here: ownership transferred to the closure
# print(data) # Uncomment for error: 'data' is uninitialized after move
```
Move capture avoids a copy entirely. The value moves into the
closure's storage. This is useful for types that are expensive to
copy or for transferring unique ownership.
:::note
The `^` transfer operator only works with `var` or a bare name in
capture lists. `{mut name^}`, `{ref name^}`, and `{imm name^}` are
compiler errors.
:::
## Copyable closures: `var^`
The `{var^}` capture list moves all referenced outer values into the
closure. When the captured types are `Copyable`, the closure value also
becomes copyable.
This allows the closure itself to be assigned to new variables or
passed by value.
```mojo
def main():
var label = "sensor-1"
def tag() {var^} -> String:
return label
var also_tag = tag # copies the closure (and its captures)
print(tag()) # sensor-1
print(also_tag()) # sensor-1
```
:::note
When you copy a closure created with `{var^}`, each captured value
is copied again through its copy initializer. For closures that
capture large or expensive values, be aware of the cost.
:::
Without `{var^}`, closures can't be assigned to new variables or
copied.
## Caller-determined mutability: `ref`
Use `{ref name}` when the closure's mutability depends on the
caller's context. If the caller provides a mutable reference, the
closure captures mutably. If immutable, the closure captures
immutably.
The following example uses `comptime if origin_of(items).mut` to inspect
how the closure captures the value at each call site:
```mojo
def show_mutability(ref items: List[Int]):
def report() {ref items}:
comptime if origin_of(items).mut:
print("mut")
else:
print("immut")
report()
# Show immutability: `xs` uses the default `imm` argument convention
def from_imm(xs: List[Int]):
show_mutability(xs) # xs is an immutable reference here
# Show mutability: `xs` uses the `mut` argument convention
def from_mut(mut xs: List[Int]):
show_mutability(xs) # xs is a mutable reference here
def main():
var nums: List[Int] = [10, 20, 30]
from_imm(nums) # immut
from_mut(nums) # mut
```
`{ref name}` doesn't choose a mutability. It shares the captured `name`'s
existing origin. The mutability is whatever that origin already carries,
decided wherever `name` was bound. This is often the function's own `ref`
parameter, ultimately resolved at the call site.
:::note
`ref` captures are an advanced feature for writing parameterized code
that works across mutability contexts. For most closures, `imm`
or `mut` is the right choice.
:::
## Empty capture list: `{}`
An empty capture list means the closure uses nothing from its surrounding
scope. It's a plain function that happens to be defined inside
another function:
```mojo
def main():
def doubled(x: Int) {} -> Int:
return x * 2
print(doubled(5)) # 10
```
The body may only use its own arguments. Referencing any outer value is a
compile error:
```mojo
# This example doesn't compile
def main():
var a = 42
def wrong() {}:
print(a) # error: no capture convention for 'a'
```
## Mixing capture conventions
A capture list is a comma-separated sequence of independent entries.
Each entry specifies its own convention, and conventions don't carry
over from one entry to the next.
```mojo
def main():
var config = "prod"
var count = 0
var label = "run-1"
def process() {imm config, mut count, var label}:
count += 1
print(config, count, label)
process() # prod 1 run-1
label = "run-2"
process() # prod 2 run-1 (label was copied at definition time)
```
Each entry is self-contained: `imm config` is a read-only reference,
`mut count` is a mutable reference, and `var label` is a copy. A bare name
without a convention keyword defaults to `imm`:
```mojo
def main():
var x = 10
def show() {x}: # same as {imm x}
print(x)
show() # 10
```
### Setting a default convention
A convention list can mix implicit and explicit entries, such as
`{imm, mut count, var label}`. You may use at most one implicit
entry per capture list, and you can place the entries in any order.
`{mut count, var label, imm}` is equivalent to
`{var label, imm, mut count}`.
For example:
```mojo
def main():
var a = 1
var b = 2
var z = "snapshot"
def mixed() {mut, var z}:
a += 10
b += 20
print(a, b, z)
mixed() # 11 22 snapshot
z = "changed"
mixed() # 21 42 snapshot
# z was copied at def-time
# Changes to outer z don't reach the closure
```
## Closures in practice
### Configurable behavior
Closures let you build specialized behavior from general-purpose parts.
The following parameterized function accepts any callable with the expected
signature. The closure carries the configuration:
```mojo
# `G` matches any `def(String) -> None` callable
def greet_all[G: def(String) -> None](names: List[String], greet: G):
for n in names:
greet(n)
def main():
var names: List[String] = ["Alice", "Bob"]
var greeting = "Hello"
def greeter(name: String) {imm greeting}:
print(greeting + ", " + name + "!")
greet_all(names, greeter)
# Hello, Alice!
# Hello, Bob!
greeting = "Hi"
greet_all(names, greeter)
# Hi, Alice!
# Hi, Bob!
```
The inner function `greeter` captures `greeting` as an immutable read-only
reference.
`greet_all` doesn't know anything about the greeting itself. It only
knows how to call a function with the type
`def(String) -> None`.
Because the closure captures `greeting` by reference instead of by
copy, changes to `greeting` between calls are visible inside the
closure.
### Accumulating state
Closures with `mut` captures can build up results across
multiple calls.
```mojo
def main():
var log = List[String]()
def record(event: String) {mut log}:
log.append(event)
record("started")
record("processed item")
record("finished")
for entry in log:
print(entry)
# started
# processed item
# finished
```
The closure `record` mutates `log` in the outer scope. Each call
appends to the same list without passing it as an argument.
---
## Lambda expressions
A **lambda function** is a small, anonymous function. Like a named
function, it accepts arguments, returns a value, and can capture values
from the surrounding scope. The difference is that you can write a lambda
exactly where it's used instead of giving it a separate declaration.
Lambdas are most useful when an algorithm stays the same but one small
piece of its behavior changes. A transformation needs to know how to
convert values. A sort needs to know how to compare them. A validator
needs to decide whether a value satisfies a rule. A callback needs to know
what to do when another part of the program invokes it.
You could write a separate named function for each of these needs, but
when the behavior is short and used in only one place, a lambda keeps it
next to the algorithm that uses it. The algorithm stays easy to read,
and the behavior doesn't need a permanent name.
## Creating a lambda
A lambda expression looks like a function declaration without a name:
```mojo
lambda (x: Int) -> Int: x + 1
```
The body is always a single expression. When you call the lambda, the
expression is evaluated and its result returned:
```mojo
var inc = lambda (x: Int) -> Int: x + 1
print(inc(4)) # 5
```
They're the same behavior you'd expect from a named function with less ceremony:
```mojo
def inc(x: Int) -> Int:
return x + 1
print(inc(4)) # 5
```
### Inline lambdas
A lambda doesn't have to be assigned to a variable. You can write it
directly as an argument when a function needs a small piece of custom
behavior:
```mojo
transform(
lambda (x: Int) -> Int: x + 1,
values
)
```
Inline lambdas work best when behavior is short and obvious. As they grow,
assigning them to a local variable often makes the surrounding code easier
to read.
When passing lambdas to other functions, you bind the lambda as an
argument. This allows the function to call it wherever that behavior is
needed. It can also pass the lambda to other functions or use it in
recursive calls. For example, a search algorithm can carry a lambda through
recursion to provide lightweight, customizable pattern matching.
### Using lambdas for side effects
You can use lambdas to call a function for each element in a collection
without producing a result. This is useful for side effects, such as
updating state. Lambda return types are optional. When omitted, they
default to None:
```mojo
# `histogram`, defined in a later example, is a dictionary of counts
var collector = lambda (n: Int) {mut histogram}: increment(histogram, n)
apply(collector, counts)
```
## Higher-order functions
Higher-order functions separate algorithms from custom logic supplied by
the caller. When you scaffold an algorithm, you can delegate the parts that
change to the caller. Lambda expressions are the perfect way to define that
behavior.
Imagine you're converting a collection of values to a new type. The
algorithm knows how to visit every element, build a new collection, and
return the result. It doesn't know how each value should be converted.
Using a lambda lets you customize behavior at the callsite without
changing the transformation function. You choose any effect so long
as the shape of the lambda matches:
```mojo
# Convert an Int to c_int
lambda (value: Int) -> c_int: c_int(value)
# Double an Int value
lambda (value: Int) -> Int: value * 2
```
All higher-order functions in Mojo share one thing in common: they accept
functions as arguments, not parameters, using infer-only typing.
### Bubble sort
Lambdas make it easy to wrap a comparator. Consider this bubble sort
implementation. `F` describes the shape of a user-supplied comparison
function:
```mojo
def bubble_sort[
T: ImplicitlyCopyable & Deinitable, F: def(T, T) -> Bool, //
](compare_fn: F, mut values: List[T]):
for end in reversed(range(len(values))):
for i in range(end):
if compare_fn(values[i], values[i + 1]):
values[i], values[i + 1] = values[i + 1], values[i]
```
You can define a free function to compare values, and pass them to
`bubble_sort`:
```mojo
def ascending(x: Int, y: Int) -> Bool:
return x > y
def main():
var values: List[Int] = [3, 1, 4, 1, 5, 9]
bubble_sort(ascending, values) # [1, 1, 3, 4, 5, 9]
```
Or, you can write the comparison inline with a lambda:
```mojo
bubble_sort(lambda (a: Int, b: Int) -> Bool: a > b, values)
```
It's the same result, with no function declaration. This is a key lambda
feature. You can write behavior without giving it a name or exposing it
through a permanent API, and your algorithm can use it immediately.
Flip the comparison from greater-than to less-than to sort in the opposite
order.
## Thin lambdas and parameters
A thin lambda carries no state. That means, it won't capture values from
the surrounding scope and it doesn't declare a compile-time parameter list
with unbound values. It's just a one-expression function written in-line.
Consider this transformation function. It uses a thin function pointer
parameter to transform each element of a list:
```mojo
def inplace_transform[
T: ImplicitlyCopyable & Deinitable, //, f: def(T) thin -> T
](mut list: List[T]):
for index in range(len(list)):
list[index] = f(list[index])
```
Notice how the function pointer is passed as a parameter and declared with
the `thin` effect.
You can call `inplace_transform` with a simple algorithm to double each
value:
```mojo
def main():
var numbers: List[Int] = [1, 2, 3, 4, 5]
inplace_transform[lambda (x: Int) -> Int: x * 2](numbers)
print(t"transformed numbers: {numbers}") # [2, 4, 6, 8, 10]
```
This example works because the lambda doesn't capture state, so it isn't
a *closure*. Contrast this with the next example, which won't compile:
```mojo
var factor = 3
inplace_transform[lambda (x: Int) -> Int: x ** factor](numbers)
```
`factor` is declared in the same scope as the lambda, and the lambda
captures it. This one thing makes the lambda a closure and can't be used at
compile-time as the function pointer parameter needed by
`inplace_transform()`.
## Lambdas and closures
Lambdas that capture values from the surrounding scope are closures.
Instead of passing values into the lambda, the closure retrieves them
from the surrounding context, allowing you to write more concise code.
You specify the convention used to capture and manipulate values in the
lambda expression. When not specified, this defaults to immutable
references (`imm`). You can read more about closure conventions in the
[Mojo language
reference](/docs/reference/closure-declarations/#capture-conventions).
There are two ways to use lambda closures: direct calls, and runtime
arguments.
### Using closures with direct calls
In this example, the lambda closure captures `x` and `y` from the
surrounding scope. The lambda is called immediately, and the result is
returned. Updating the values of `x` and `y` and calling the lambda again
returns a new result:
```mojo
var x, y = 3.0, 4.5
var magnitude = (
lambda -> Float64: (x**2 + y**2) ** 0.5
)
var distance = magnitude()
print(t"distance of ({x}, {y}): {distance}") # 5.408326913175031
x, y = -2.5, 1.5
distance = magnitude()
print(t"distance of ({x}, {y}): {distance}") # 2.9154759474226504
```
### Runtime arguments
Runtime arguments can accept both thin lambdas and closures. Here's a
`transform()` function that uses a runtime argument, with an infer-only
function type:
```mojo
def transform[
T: Copyable, U: Copyable,
F: def(T) -> U, //
](f: F, list: List[T]) -> List[U]:
return [f(item) for item in list]
```
In the preceding section, the following lambda closure wouldn't compile
because it was passed at compile-time to a parameter, which doesn't accept
closures. `transform()` uses a runtime function argument. Now, the code
compiles and runs:
```mojo
var numbers: List[Int] = [2, 4, 6, 8, 10]
var factor = 3
var transformed = transform(lambda (x: Int) -> Int: x**factor, numbers)
print(t"transformed numbers: {transformed}") # [8, 64, 216, 512, 1000]
factor = 2
transformed = transform(lambda (x: Int) -> Int: x**factor, numbers)
print(t"transformed numbers: {transformed}") # [4, 16, 36, 64, 100]
```
## Capturing and mutating state
A closure can update the values it captures without mentioning those values
in its own code. This next example showcases lambdas to create a histogram
of word lengths.
The code starts with an `apply()` function. It calls a function for each
member of a list. As you can see from its function type (`F`), it takes
lambdas that don't return a value. The lambda is called for its side
effects, not for the value of its expression:
```mojo
def apply[T: Copyable, F: def(T) -> None, //](f: F, i: List[T]):
for item in i:
f(item)
```
In this example, a closure will update a captured histogram dictionary. It
does this by calling `increment[]()`. This function updates a dictionary by
increasing the value for a given key by one:
```mojo
def increment[
Key: ImplicitlyCopyable & Hashable & Equatable & Deinitable
](mut d: Dict[Key, Int], key: Key):
d[key] = d.get(key, 0) + 1
```
This example is given a string of words. It removes punctuation and splits
the string into a word list. Then, it counts the length of each word using
a lambda:
```mojo
var words = String(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
"Sed fringilla nons sapien quis pharetra."
).replace(",", "").replace(".", "")
var word_list = [String(w) for w in words.split(" ")]
print(t"word_list: {word_list}")
# Count each word
var counter = lambda (x: String) -> Int: x.count_codepoints()
var counts = transform(counter, word_list)
print(t"Initial counts: {counts}")
# [5, 5, 5, 3, 4, 11, 10, 4, 3, 9, 4, 6, 4, 8]
```
To build the histogram, the next lambda captures the histogram dictionary
and uses `increment[]()` to update the count for each word length:
```mojo
# Create a histogram of the counts
var histogram: Dict[Int, Int] = {}
var collector = lambda (n: Int) {mut histogram}: increment(histogram, n)
apply(collector, counts)
print(t"Histogram: {histogram}")
# {5: 3, 3: 2, 4: 4, 11: 1, 10: 1, 9: 1, 6: 1, 8: 1}
```
The `mut` capture establishes that the lambda can mutate the captured
variable. `apply()` calls this lambda for each word length, updating
the histogram dictionary as it goes.
A final lambda transforms each count into stars. It's the kind of
effortless transformation that makes lambdas so useful:
```mojo
var stars = lambda (n: Int) -> String: "*" * n
for key in histogram.keys():
print(t"{key}: {stars(histogram.get(key, 0))}")
```
## Lambdas and FFI interop
Thin lambdas work well with C FFI. This final example uses a lambda to sort
a list of integers using the C standard library `qsort()` function. The
lambda is passed as the comparator to `qsort()` to sort the list in
ascending order:
```mojo
from std.ffi import external_call, c_int, c_size_t
from std.sys import size_of
def main():
var values: List[Int] = [5, 3, 11, 10, 9, 6, 4]
# Transform is defined earlier on this page
var c_values: List[c_int] = transform(
lambda (v: Int) -> c_int: c_int(v), values
)
external_call["qsort", NoneType](
c_values.unsafe_ptr(), # values are passed as an opaque pointer
c_size_t(len(c_values)),
c_size_t(size_of[c_int]()),
lambda (
a: MutOpaquePointer[MutUntrackedOrigin],
b: MutOpaquePointer[MutUntrackedOrigin],
) abi("C") -> c_int: a.unsafe_bitcast[c_int]()[]
- b.unsafe_bitcast[c_int]()[]
)
# Bitcasting retrieves the `c_int` values from the opaque pointer
print(t"Sorted keys: {c_values}") # [3, 4, 5, 6, 9, 10, 11]
```
Don't miss first lambda call shown in this example. It converts a list
of integers to a list of `c_int` values with `transform()` before passing
them to "qsort".
---
## Functions
Mojo uses the `def` keyword to define functions.
## Anatomy of a function
A Mojo function declaration can include the following elements:
def function_name [
parameters ...
](
arguments ...
) -> return_value_type :
function_body
Functions can have:
- Parameters: A function can optionally take one or more compile-time
_parameter_ values used for metaprogramming.
- Arguments: A function can also optionally take one or more run-time
_arguments_.
- Return value: A function can optionally return a value.
- Function body: Statements that run when you call the function.
Function definitions must include a body.
You can omit all of the optional parts of the function, so the minimal
function is something like this:
```mojo
def do_nothing():
pass
```
If a function takes no parameters, you can omit the square brackets, but the
parentheses are always required.
Although you can't leave out the function body, you can use the `pass` statement
to define a function that does nothing.
:::note Struct methods
Functions declared inside a struct are called _methods_. They can include the
same elements as regular functions, but follow a few extra rules. For more
information, see the page on [structs](/docs/manual/structs/).
:::
### Arguments and parameters
Functions take two kinds of inputs: _arguments_ and _parameters_. Arguments are
familiar from many other languages: they are run-time values passed into the
function.
```mojo
def max(a: Int, b: Int) -> Int:
return a if a > b else b
```
On the other hand, you can think of a parameter as a compile-time variable that
becomes a run-time constant. For example, consider the following function with a
parameter:
```mojo no-test
def add_tensors[rank: Int](a: MyTensor[rank], b: MyTensor[rank]) -> MyTensor[rank]:
# ...
```
In this case, the `rank` value needs to be specified in a way that can be
determined at compilation time, such as a literal or expression.
When you compile a program that uses this code, the compiler produces a unique
version of the function for each unique `rank` value used in the program, with
`rank` treated as a constant within each specialized version.
This usage of "parameter"
is probably different from what you're used to from other languages, where
"parameter" and "argument" are often used interchangeably. In Mojo, "parameter"
and "parameter expression" refer to compile-time values, and "argument" and
"expression" refer to run-time values.
By default, both arguments and parameters can be specified either by position or
by keyword. These forms can also be mixed in the same function call.
```mojo
# positional
var x = max(5, 7) # Positionally, a=5 and b=7
# keyword
var y = max(b=3, a=9)
# mixed
var z = max(5, b=7) # Positionally, a=5
```
For more information on arguments, see [Function arguments](#function-arguments)
on this page. For more information on parameters, see
[Parameterization: compile-time metaprogramming](/docs/manual/parameters/).
## Function requirements
A function has the following requirements:
- You must declare the type of each function parameter and argument.
- If a function doesn't return a value, you can either omit the return type or
declare `None` as the return type.
```mojo no-test
# The following function definitions are equivalent
def greet(name: String):
print("Hello,", name)
def greet(name: String) -> None:
print("Hello,", name)
```
- If the function returns a value, you must either declare the return type using
the -> type syntax or provide a
[named result](#named-results) in the argument list.
```mojo no-test
# The following function definitions are equivalent
def incr(a: Int) -> Int:
return a + 1
def incr(a: Int, out b: Int):
b = a + 1
```
For more information, see the [Return values](#return-values) section of this
page.
## Function arguments
:::note Functions with / and * in the argument list
You might see the following characters in
place of arguments: slash (`/`) and/or star (`*`). For example:
```mojo no-test
def myfunc(pos_only, /, pos_or_keyword, *, keyword_only):
```
Arguments **before** the `/` can be passed only by position. Arguments **after**
the `*` can be passed only by keyword. For details, see
[Positional-only and keyword-only arguments](#positional-only-and-keyword-only-arguments)
You may also see argument names prefixed with one or two stars (`*`):
```mojo no-test
def myfunc2(*names, var **attributes):
```
An argument name prefixed by a single star character, like `*names` identifies a
[variadic argument](#variadic-arguments), while an argument name prefixed with
a double star, like `**attributes` identifies a
[variadic keyword-only argument](#variadic-keyword-arguments).
:::
### Optional arguments
An optional argument is one that includes a default value, such as the `exp`
argument here:
```mojo
def my_pow(base: Int, exp: Int = 2) -> Int:
return base**exp
def use_defaults():
# Uses the default value for `exp`
var z = my_pow(3)
print(z)
```
However, you can't define a default value for an argument that's declared with
the [`mut`](/docs/manual/values/ownership/#mutable-arguments-mut) argument
convention.
Any optional arguments must appear after any required arguments. [Keyword-only
arguments](#positional-only-and-keyword-only-arguments), discussed later, can
also be either required or optional.
### Keyword arguments
You can also use keyword arguments when calling a function. Keyword arguments
are specified using the format
argument_name = argument_value .
You can pass keyword arguments in any order:
```mojo duplicate-of=optional-args
def my_pow(base: Int, exp: Int = 2) -> Int:
return base**exp
def use_keywords():
# Uses keyword argument names (with order reversed)
var z = my_pow(exp=3, base=2)
print(z)
```
### Variadic arguments
Variadic arguments let a function accept a variable number of arguments. To
define a function that takes a variadic argument, use the variadic argument
syntax *argument_name :
```mojo
def sum(*values: Int) -> Int:
var sum: Int = 0
for value in values:
sum = sum + value
return sum
```
The variadic argument `values` here is a placeholder that accepts any number of
passed positional arguments.
You can define zero or more arguments before the variadic argument. When calling
the function, Mojo assigns any remaining positional arguments to the variadic
argument, so any arguments declared **after** the variadic argument can only be
specified by keyword (see
[Positional-only and keyword-only arguments](#positional-only-and-keyword-only-arguments)).
Variadic arguments fall into two categories:
- Homogeneous variadic arguments, where all of the passed arguments are the same
type—all [`Int`](/docs/std/simd/#int), or all
[`String`](/docs/std/collections/string/string/String/), for example.
- Heterogeneous variadic arguments, which can accept a set of different argument
types.
The following sections describe how to work with homogeneous and heterogeneous
variadic arguments.
:::note Variadic parameters
Mojo also supports variadic _parameters_, but with some limitations—for details
see [variadic parameters](/docs/manual/parameters/#variadic-parameters).
:::
#### Homogeneous variadic arguments
When defining a homogeneous variadic argument (all arguments must be the same
type), use *argument_name : argument_type :
```mojo
def greet(*names: String):
...
```
Inside the function body, the variadic argument is available as an iterable list
for ease of use. Concretely, that type is named
[`VariadicList`](/docs/std/builtin/variadics/VariadicList/). Here is a
simple example:
```mojo
def sum(*values: Int) -> Int:
var sum: Int = 0
for value in values:
sum = sum + value
return sum
```
Iterating over this list directly with a `for..in` loop currently produces a
reference to the element, which can be mutable with a `mut` variadic list. Use
the `ref` binding pattern to capture a mutable reference if you
want to mutate the elements of the list:
```mojo
def make_worldly(mut *strs: String):
for ref i in strs:
i += " world"
```
You can also directly index the list with integers as well:
```mojo
def make_worldly2(mut *strs: String):
for i in range(len(strs)):
strs[i] += " world"
```
#### Heterogeneous variadic arguments
Implementing heterogeneous variadic arguments (each argument type may be
different) is somewhat more complicated than homogeneous variadic arguments. To
handle multiple argument types, the function must be
[parameterized](/docs/manual/generics/), which requires using
[traits](/docs/manual/traits/) and [parameters](/docs/manual/parameters/). So
the syntax may look a little unfamiliar if you haven't worked with those
features.
The signature for a function with a heterogeneous variadic argument looks like
this:
```mojo no-test
def count_many_things[*ArgTypes: Intable](*args: *ArgTypes):
...
```
The parameter list, `[*ArgTypes: Intable]` specifies that the function takes an
`ArgTypes` parameter, which is a list of types, all of which conform to the
[`Intable`](/docs/std/builtin/int/Intable/) trait. The asterisk in `*ArgTypes`
indicates that `ArgTypes` is a **variadic type parameter** (a list of types).
The argument list, `(*args: *ArgTypes)` has the familiar `*args` for the
variadic argument, but instead of a single type, its type is defined as the
variadic type list `*ArgTypes`. The asterisk in `*args` indicates a
**variadic argument**, and the asterisk in `*ArgTypes` refers to the
variadic type parameter.
This means that each argument in `args` has a corresponding type in `ArgTypes`,
so args[n ] is of type
ArgTypes[n ].
Inside the function, `args` becomes a
[`VariadicPack`](/docs/std/builtin/variadics/VariadicPack/) because the
syntax `*args: *ArgTypes` creates a heterogeneous variadic argument. That
means each element in `args` can be a different type that requires a
different amount of memory. To iterate through the `VariadicPack`, the
compiler must know each element's type, so you must use a [`comptime for`
loop](/docs/manual/metaprogramming/comptime-evaluation/#comptime-for):
```mojo
def count_many_things[*ArgTypes: Intable](*args: *ArgTypes) -> Int:
var total = 0
comptime for i in range(args.__len__()):
total += Int(args[i])
return total
def main():
print(count_many_things(5, 11.7, 12)) # 28
```
Notice that when calling `count_many_things()`, you don't actually pass in
a list of argument types. You only need to pass in the arguments, and Mojo
generates the `ArgTypes` list itself.
#### Variadic keyword arguments
Mojo functions also support variadic keyword arguments (`**kwargs`). Variadic
keyword arguments let you pass an arbitrary number of keyword
arguments. To define a function that takes a variadic keyword argument, use the
variadic keyword argument syntax var **kw_argument_name :
```mojo
def print_nicely(var **kwargs: Int):
for item in kwargs.items():
print(item.key, "=", item.value)
```
Calling it with any number of keyword arguments prints each one:
```mojo
# prints:
# `a = 7`
# `y = 8`
print_nicely(a=7, y=8)
```
In this example, the argument name `kwargs` is a placeholder that accepts any
number of keyword arguments. Inside the body of the function, you can access
the arguments as a dictionary of keywords and argument values (specifically,
an instance of
[`StringDict`](/docs/std/collections/dict/StringDict/)).
There are currently a few limitations:
- Variadic keyword arguments must be declared with the `var` [argument
convention](/docs/manual/values/ownership#argument-conventions) (the
function owns the argument dictionary and may mutate it); no other
convention is supported:
```mojo no-test
# Not supported.
def imm_kwargs(imm **kwargs: Int): ...
```
- All the variadic keyword arguments must have the same type, and this
determines the type of the argument dictionary. For example, if the argument
is `var **kwargs: Float64` then the argument dictionary is a
`StringDict[Float64]`.
- The argument type must conform to the
[`Copyable`](/docs/std/traits/copyable/Copyable/) trait.
- Dictionary unpacking isn't supported yet:
```mojo no-test
def takes_dict(d: Dict[String, Int]):
print_nicely(**d) # Not supported yet.
```
- Variadic keyword _parameters_ aren't supported yet:
```mojo no-test
# Not supported yet.
def var_kwparams[**kwparams: Int](): ...
```
### Positional-only and keyword-only arguments
When defining a function, you can restrict some arguments so that they can
be passed only as positional arguments, or they can be passed only as keyword
arguments.
To define positional-only arguments, add a slash character (`/`) to the
argument list. Any arguments before the `/` are positional-only: they can't be
passed as keyword arguments. For example:
```mojo
def min(a: Int, b: Int, /) -> Int:
return a if a < b else b
```
This `min()` function can be called with `min(1, 2)` but can't be called using
keywords, like `min(a=1, b=2)`.
There are several reasons you might want to write a function with
positional-only arguments:
- The argument names aren't meaningful for the caller.
- You want the freedom to change the argument names later on without breaking
backward compatibility.
For example, in the `min()` function, the argument names don't add any real
information, and there's no reason to specify arguments by keyword.
For more information on positional-only arguments, see [PEP 570 – Python
Positional-Only Parameters](https://peps.python.org/pep-0570/).
Keyword-only arguments are the inverse of positional-only arguments: they can
be specified only by keyword. If a function accepts variadic arguments, any
arguments defined _after_ the variadic arguments are treated as keyword-only.
For example:
```mojo
def sort(*values: Float64, ascending: Bool = True):
...
```
In this example, you can pass any number of
[`Float64`](/docs/std/simd/#float64) values, optionally
followed by the keyword `ascending` argument:
```mojo
sort(1.1, 6.5, 4.3, ascending=False)
```
If the function doesn't accept variadic arguments, you can add a single star
(`*`) to the argument list to separate the keyword-only arguments:
```mojo
def kw_only_args(a1: Int, a2: Int, *, double: Bool) -> Int:
var product = a1 * a2
if double:
return product * 2
else:
return product
```
Keyword-only arguments often have default values, but this isn't required. If a
keyword-only argument doesn't have a default value, it's a _required
keyword-only argument_. It must be specified, and it must be specified by
keyword.
Any required keyword-only arguments must appear in the signature before
any optional keyword-only arguments. That is, arguments appear in the following
sequence in a function signature:
- Required positional arguments.
- Optional positional arguments.
- Variadic arguments.
- Required keyword-only arguments.
- Optional keyword-only arguments.
- Variadic keyword arguments.
For more information on keyword-only arguments, see [PEP 3102 – Keyword-Only
Arguments](https://peps.python.org/pep-3102/).
## Overloaded functions
All function declarations must specify argument types, so if you want a
function to work with different data types, you need to implement
separate versions of the function that each specify different argument types.
This is called "overloading" a function.
For example, here's an overloaded `add()` function that can accept either
`Int` or `String` types:
```mojo
def add(x: Int, y: Int) -> Int:
return x + y
def add(x: String, y: String) -> String:
return x + y
```
If you pass anything other than `Int` or `String` to the `add()` function,
you'll get a compiler error. That is, unless `Int` or `String` can implicitly
cast the type into their own type. For example, `String` includes an overloaded
version of its initializer (`__init__()`) that supports
[implicit conversion](/docs/manual/lifecycle/life/#constructors-and-implicit-conversion)
from a [`StringLiteral`](/docs/std/builtin/string_literal/StringLiteral/) value.
Thus, you can also pass a `StringLiteral` to a function that expects a `String`.
When resolving an overloaded function call, the Mojo compiler picks the
candidate that best fits the call according to the rules in
[Overload resolution](#overload-resolution), or reports the call as
ambiguous if no single candidate is best.
:::note Overload sets
An "overload set" is a collection of function overloads that share
the same name but different signatures.
- Overload sets can't be extended by imports, aliases, or parameters.
- To avoid issues with local functions using the same name as an imported
one, use an aliased import.
`from package import foo as imported_foo` won't conflict with a local
function named `foo`.
:::
### Overload resolution
When resolving an overloaded function, Mojo looks at:
- The number, position, and keyword of each argument.
- The type of each argument and each compile-time parameter.
- The argument conventions on each argument.
- Whether the candidate is an instance method or
[static method](/docs/manual/structs/#static-methods).
- Whether an initializer allows
[implicit conversion](/docs/manual/lifecycle/life/#constructors-and-implicit-conversion).
Mojo does **not** look at the return type, the `raises` effect, or any
context surrounding the call. Two functions that differ only in return
type or in whether they `raises` are duplicate definitions—the compiler
rejects the second declaration.
The overload resolution logic filters for candidates according to the following
rules, in order of precedence:
1. Candidates requiring the smallest number of implicit conversions (in both
arguments and parameters).
2. Candidates without variadic arguments.
3. Candidates without variadic parameters.
4. Candidates with the shortest parameter signature.
5. Non-`@staticmethod` candidates (over `@staticmethod` ones, if available).
If the compiler can't figure out which function to use, you can resolve the
ambiguity by explicitly casting your value to a supported argument type. For
example, the following code calls the overloaded `foo()` function,
but both implementations accept an argument that supports [implicit
conversion](/docs/manual/lifecycle/life#constructors-and-implicit-conversion)
from `String`. So, the call to `foo("Hello")` is ambiguous and creates a
compiler error. You can fix this by casting the value to the type you really
want:
```mojo
struct MyString:
@implicit
def __init__(out self, string: String):
pass
struct YourString:
@implicit
def __init__(out self, string: String):
pass
def foo(name: MyString):
print("MyString")
def foo(name: YourString):
print("YourString")
def call_foo():
# Both `foo` overloads can accept `"Hello"`, so Mojo doesn't know
# which one to call.
foo(MyString("Hello"))
```
For the full overload-resolution rules and edge cases, see the
[function declarations
reference](/docs/reference/function-declarations/#function-overloads).
## Return values
Return value types are declared in the signature using the
-> type syntax. Values are
passed using the `return` keyword, which ends the function and returns the
identified value (if any) to the caller.
```mojo
def get_greeting() -> String:
return "Hello"
```
By default, the value is returned to the caller as an owned value. As with
arguments, a return value may be [implicitly
converted](/docs/manual/lifecycle/life#constructors-and-implicit-conversion) to
the named return type. For example, the previous example calls `return` with a
string literal, `"Hello"`, which is implicitly converted to a `String`.
:::note Returning a reference
A function can also return a mutable or immutable reference using a `ref` return
value. For details, see
[Lifetimes, origins, and references](/docs/manual/values/lifetimes/).
:::
### Named results
Named function results allow a function to return a value that can't be moved or
copied. Named result syntax lets you specify a named, uninitialized variable to
return to the caller using the `out` argument convention:
```mojo
def get_name_tag(var name: String, out name_tag: NameTag):
name_tag = NameTag(name^)
```
The `out` argument convention identifies an uninitialized variable that the
function must initialize. (This is the same as the `out` convention used in
[struct initializers](/docs/manual/lifecycle/life/#constructor).) The `out`
argument for a named result can appear anywhere in the argument list, but by
convention, it should be the last argument in the list.
A function can declare only one return value, whether it's declared using an
`out` argument or using the standard ->
type syntax.
A function with a named result argument doesn't need to include an explicit
`return` statement, as shown above. If the function terminates without a
`return`, or at a `return` statement with no value, the value of the `out`
argument is returned to the caller. If it includes a `return` statement with a
value, that value is returned to the caller, as usual.
The fact that a function uses a named result is transparent to the caller. That
is, these two signatures are interchangeable to the caller:
```mojo no-test
def get_name_tag(var name: String) -> NameTag:
...
def get_name_tag(var name: String, out name_tag: NameTag):
...
```
In both cases, the call looks like this:
```mojo
var tag = get_name_tag("Judith")
```
Because the return value is assigned to this special `out` variable, it doesn't
need to be moved or copied when it's returned to the caller. This means that you
can create a function that returns a type that can't be moved or copied, and
which takes several steps to initialize:
```mojo
struct ImmovableObject:
var name: String
def __init__(out self, var name: String):
self.name = name^
def create_immovable_object(var name: String, out obj: ImmovableObject):
obj = ImmovableObject(name^)
obj.name += "!"
# obj is implicitly returned
```
To the caller, it's an ordinary function call:
```mojo
var my_obj = create_immovable_object("Blob")
```
By contrast, the following function with a standard return value doesn't work:
```mojo no-test
def create_immovable_object2(var name: String) -> ImmovableObject:
var obj = ImmovableObject(name^)
obj.name += "!"
return obj^ # Error: ImmovableObject is not copyable or movable
```
Because `create_immovable_object2` uses a local variable to store the object
while it's under construction, the return call requires it to be either moved
or copied to the callee. This isn't an issue if the newly-created value is
returned immediately:
```mojo
def create_immovable_object3(var name: String) -> ImmovableObject:
return ImmovableObject(name^) # OK
```
## Raising and non-raising functions
By default, when a function raises an error, the function terminates immediately
and the error propagates to the calling function. If the calling function
doesn't handle the error, it continues to propagate up the call stack.
```mojo
def raises_error() raises:
raise Error("There was an error.")
```
Mojo functions are _non-raising_ by default. To declare that a function can
propagate an error to its caller, add the `raises` keyword to the function
signature. A non-raising function that calls a raising function **must handle
any possible errors**.
```mojo no-test
# This function will not compile
def unhandled_error():
raises_error() # Error: can't call raising function in a non-raising context
# Explicitly handle the error
def handle_error():
try:
raises_error()
except e:
print("Handled an error:", e)
# Explicitly propagate the error
def propagate_error() raises:
raises_error()
```
All of the examples above use the built-in
[`Error`](/docs/std/builtin/error/Error/) type. Mojo also supports
_typed errors_, where you specify a custom error type a function can raise:
```mojo no-test
def validate(value: Int) raises ValidationError -> Int:
...
```
For more information, see
[Errors, error handling, and context managers](/docs/manual/errors/).
---
## Parameterized declarations
Parameterized types let you write code once and use it across many types
without duplicating logic. You don't need separate implementations or
type checks for each case. Mojo generates specialized versions for each
type you use.
Most languages only parameterize over types. Mojo also supports value
parameters. Its parameter system accepts both types and compile-time values
using the same `[]` syntax.
Mojo distinguishes compile-time parameters from runtime arguments in its
syntax. Parameters go in square brackets `[]` and resolve at compile
time. Arguments go in parentheses `()` and resolve at runtime.
You see this distinction at every definition and call site, so you always
know what the compiler specializes and what gets passed at runtime.
```mojo
# T is a type parameter, threshold is a value parameter.
# Both are compile-time. values is a runtime argument.
def count_above[
T: Comparable & ImplicitlyCopyable & Deinitable, threshold: T
](values: List[T]) -> Int:
var count = 0
for v in values:
if v > threshold:
count += 1
return count
```
Many parameterized declarations use traits to constrain which types work with
the code. A trait defines what a type must do, and parameterized code declares
which traits it requires. The compiler enforces these requirements and generates
specialized code for each concrete type at the call site.
## Type parameters {#type-generics}
Type parameters let you write code that works across many types. You define
behavior once, and the compiler specializes it for each concrete type at
the call site.
### Type constraints
*Constraints* define what a type must do. You express them as traits or trait
compositions. You must always constrain or explicitly type parameter names.
Without a fixed set of required features, the compiler has no guarantees about
what operations are valid.
- The most permissive constraint is
[`AnyType`](/docs/std/traits/anytype/AnyType/). It places no behavioral
requirements on a type.
- [`Deinitable`](/docs/std/traits/deinitable/Deinitable/)
is a common baseline for types with lifetimes. Parameterized code that stores
or owns values often requires it.
Constraints make your code sound: every operation you use is guaranteed
to exist for any type that satisfies them.
### Naming conventions
Mojo follows naming conventions used in languages like Rust and C++.
Type parameter names use PascalCase, short (`T`, `E`) or descriptive
(`ErrorType`, `Element`). By convention, `T`, `U`, `V` are general
types; `K`/`V` for key-value pairs; `E` for errors; `H` for hashers.
Value parameter names use lower_snake_case and should be descriptive
(`capacity`, `hasher`, `tile_x`).
### Basic example: compare two lists
Consider comparing two lists to test whether they contain the same values
in the same order.
You could write a separate implementation for each element type. Or you
can write one parameterized function that works for any list whose elements
support the operations you need.
This concrete version only works with integers:
```mojo
def all_equal_int(ref lhs: List[Int], ref rhs: List[Int]) -> Bool:
if len(lhs) != len(rhs): return False
for left, right in zip(lhs, rhs):
if left != right:
return False
return True
```
The parameterized version doesn't care about the element type. It only requires
the capabilities the algorithm uses: elements must support equality
comparison, be copyable, and be implicitly destructible (so the tuples
`zip()` yields can be cleaned up at the end of each iteration):
```mojo
def all_equal[
T: Equatable & Copyable & Deinitable
](ref lhs: List[T], ref rhs: List[T]) -> Bool:
if len(lhs) != len(rhs): return False
for left, right in zip(lhs, rhs):
if left != right:
return False
return True
```
Both implementations follow the same logic: check lengths, return `False`
on the first mismatch, and return `True` if no differences are found.
The type parameter `T` is declared in square brackets before the function
arguments. It represents the element type for both lists. When you call
`all_equal()`, the compiler infers `T` from the call site:
```mojo
print("Int (Expect True):\t",
all_equal([1, 2, 3], [1, 2, 3])) # True
print("Int (Expect False):\t",
all_equal([1, 2, 3], [4, 5, 6])) # False
print("String (Expect True):\t",
all_equal(["hello", "world"], ["hello", "world"])) # True
print("String (Expect False):\t",
all_equal(["hello", "world"], ["goodbye", "world"])) # False
```
The compiler generates a concrete, type-specific version of
`all_equal()` for each type you use: one for `Int` and one for
`String` in this example.
:::note
In type theory, the parametric `all_equal()` function is
*polymorphic*. The generated type-specific versions are *monomorphic*.
:::
### Choosing constraints
Keep requirements minimal:
```mojo
T: Equatable & Copyable & Deinitable
```
The ampersand (`&`) composes traits. Use the fewest constraints your code
needs. This keeps your function usable with more types. If you remove
`Equatable` from `all_equal()`, the code won't compile because the
compiler can't guarantee that `!=` exists for all `T`. Dropping
`Deinitable` fails for a subtler reason: `zip()` yields each pair
as a tuple, and the loop can only destroy that temporary tuple if `T` is
implicitly destructible.
:::note
A type supports `!=` by implementing `__ne__()`.
:::
When your code uses an operation not covered by its constraints, the
compiler reports an error: the type is *underspecified*. Fix this by
adding the trait that provides the missing behavior. In practice, parameterized
errors mean your constraints don't include the behavior your code uses.
Adding constraints restricts which types you accept, but expands what
your code can do. Each trait adds guaranteed operations, which lets the
compiler check correctness and reason about lifetimes and effects.
### Parameterized types {#generic-parameter-types}
In the `all_equal()` example, both parameters use the same element type:
```mojo
lhs: List[T], rhs: List[T]
```
Using `T` for both ensures your loop compares like with like and prevents
type mismatches. You can also use a parameterized type directly without
embedding it in a container:
```mojo
def my_parameterized_fn[T: AnyType](value: T):
```
This function accepts any type because its only limit is `AnyType`, the
root of the trait hierarchy that all types conform to. Using `AnyType`
means the value has no guaranteed deinitializer or lifetime management.
Outside of [reflection](/docs/manual/metaprogramming/reflection/), this
function can't do anything meaningful with `value`.
#### Printing under-specified parameterized values
A common issue with `AnyType` is that the compiler can't print values of
unspecified types. Because it can't determine whether `T` conforms to
`Writable`, it can't generate the code needed to print it:
```mojo
def function[Ts: AnyType](*args: Ts):
for arg in args:
print(arg) # Will error.
# The compiler can't verify `Writable` conformance
def main():
function(1, 2, 3)
```
Work around this by testing the parameterized type parameter for `Writable`
conformance and downcasting to expose the `Writable` trait:
```mojo
def represent[T: AnyType](v: T) -> String:
comptime if conforms_to(T, Writable):
return String(v)
else:
return String(t"{reflect[T].name()}")
def function[Ts: AnyType](*args: Ts):
for arg in args:
print(represent(arg))
@fieldwise_init
struct SomeStruct:
var x: Int
def main():
function(1, 2, 3) # prints each integer
function(SomeStruct(2), SomeStruct(3)) # each "(module-name).SomeStruct"
```
`Writable` items print as if explicitly converted to `String`.
Non-`Writable` items use their type name, prefixed by the module name
(the file name without the extension).
Read more about [safe downcasting](#downcasting-safely) on this page.
## Parameterized types {#generic-types}
Parameterized declarations aren't limited to functions. You can define
parameterized types that use compile-time parameters to define both their fields
and their methods.
Parameterized types let you package a reusable shape: stored fields plus
supported operations. Instead of writing `PairInt`, `PairString`, and so
on, you write one `Pair[T]` and let the compiler generate specialized
versions for each concrete `T` you use.
```mojo
comptime ComparableValue = Equatable & ImplicitlyCopyable & Deinitable
@fieldwise_init
struct Pair[T: ComparableValue](ComparableValue):
var left: Self.T
var right: Self.T
def __eq__(self, other: Pair[Self.T]) -> Bool:
return self.left == other.left and self.right == other.right
```
Like parameterized functions, parameterized types use placeholder parameters,
but a parameterized type uses those parameters in its storage as well as in its
methods. Here, `Pair` stores two values of the same element type and implements
equality by comparing its fields.
`Pair` needs to compare values, so `T` must be equatable. It also needs
to copy and clean up values in common operations, so it applies a trait
composition on `T`:
```mojo
comptime ComparableValue = Equatable & ImplicitlyCopyable & Deinitable
```
The `Pair` definition applies this conformance in two places:
```mojo
struct Pair[T: ComparableValue](ComparableValue):
```
- **Square brackets** — a requirement on callers: any value used with
`Pair` must have type `T`, and `T` must be a `ComparableValue`.
- **Parentheses** — a promise from `Pair` itself: "I am a
`ComparableValue`."
This makes `Pair[T]` usable anywhere a `ComparableValue` is required,
and ensures all `T` values conform.
## Mixing type and value parameters
Parameterized types can take non-type parameters too. Add whatever you need
to define the behavior:
```mojo
struct ExampleStruct:
def example[
T: Writable & Copyable, # type parameter
count: Int, # value parameter
](
self,
data: String, # argument
init_value: T # parameterized argument
) -> String:
```
By convention, Mojo uses lower_snake_case for compile-time value
parameters. This visually distinguishes them from type parameters.
## Simplified conformance syntax with `Some`
You can replace explicit type parameters and conformances with concise
`Some[Trait(s)]` and `SomeTypeList[Trait(s)]` syntax. These forms
support both a single trait and trait compositions, and let you express
conformance where you use the type instead of declaring a parameter in
one place and using it in another.
For example:
```mojo
def my_parameterized_fn[T: Trait(s)](value: T):
```
becomes:
```mojo
def my_parameterized_fn(value: Some[Trait(s)]):
```
You can use `Some` with any trait or trait composition, under any
argument convention, wherever you've been using type parameters. If the
compiler can't infer a concrete type, it errors and asks you to use
explicit type parameters instead.
### Arguments
`Some` places the trait requirement directly on the argument, eliminating the
explicit type parameter:
```mojo
# Before
def foo[T: Intable, //](x: T) -> Int:
return x.__int__()
# After
def foo(x: Some[Intable]) -> Int:
return x.__int__()
```
### Function types
`Some` works with function types too, moving closure parameter conformance
onto the argument:
```mojo
# Before
def sync_parallelize[
FuncType: def(Int) -> None,
](func: FuncType):
...
# After
def sync_parallelize(func: Some[def(Int) -> None]):
...
```
### Variadics
Use `*SomeTypeList` to conform a variadic parameter pack instead of a
single type parameter:
```mojo
# Before
def show[*Ts: Writable](*pack: *Ts):
...
# After
def show(*pack: *SomeTypeList[Writable]):
...
```
### Operator overloads
Operator overloads are a natural fit for `Some`, the trait that enables
the operator lives directly on the declaration:
```mojo
# Before
def __getitem__[I: Indexer, //](self, idx: I) -> ref[self.x] Self.T:
...
# After
def __getitem__(self, idx: Some[Indexer]) -> ref[self.x] Self.T:
...
```
### Where `Some` won't work
`Some` can't replace type parameters in all cases. The compiler can't
infer a concrete type for struct fields, and will report an error
indicating the type isn't concrete and asking you to use `[]` to bind
missing parameters:
```mojo
@fieldwise_init
struct Struct(Writable):
# Error: a `Some` struct field has no concrete type to infer
var x: Some[Copyable & Deinitable & Writable]
def main():
var s = Struct(1)
print(s)
```
Move the conformances back to a parameterized type parameter to fix this:
```mojo
@fieldwise_init
struct Struct[T: Copyable & Deinitable & Writable](Writable):
var x: Self.T
def main():
var s = Struct(1)
print(s) # Struct[Int](x=1)
```
## Using conditional availability {#downcasting-safely}
Sometimes a conformance is broader than you need. For example, a
parameterized type may be constrained to `AnyType` in a struct, but you
want to use it as `Writable` in a given method. Add a `where` clause to
require `Writable`, allowing the compiler to prove the conformance before
allowing a concrete instance to call the method:
```mojo
def process(self, value: Self.T) where conforms_to(Self.T, Writable):
print(value)
```
Call `conforms_to(T, Trait)` to test whether `T` satisfies a trait.
Combine multiple checks with `and` or `or` to express more complex
conditions.
Conditions are not limited to trait checks. They can also constrain
compile-time facts, such as a data length being a power of two or a
capacity being positive. Any compile-time expression with a known value
can appear in a `where` clause.
The compiler rejects calls that don't satisfy the constraint. For example,
a type built with a non-writable type `T` can't call `process()`:
```mojo
self.process(non_writable) # Compile-time error, constraint violation
```
`where` clauses let the compiler prove conditions before constructing
types, resolving conformances, compiling methods and functions, or
manifesting compile-time declarations.
Beyond conformance, a where clause can test predicates on its
parameters: whether a `DType` is floating point or numeric
(`dtype.is_floating_point()`, `dtype.is_numeric()`), or whether an integer
parameter is a power of two.
What it can't do is evaluate target facts like `is_64bit()` or
`is_nvidia_gpu()`, or arbitrary compile-time functions, as constraints. The
compiler can't carry those as proof. For those cases, use `comptime if`
instead, which evaluates its condition directly.
## Parameterized values {#value-generics}
Values parameterize code over compile-time constants instead of
types. You declare them in `[]` alongside type parameters, but they bind
to values.
### When to use parameterized values {#when-to-use-value-generics}
Use parameterized values when a value shapes structure or behavior and is known
at compile time. Common cases:
- *Fixed sizes:* buffer lengths, array dimensions, matrix shapes
- *Thresholds and limits:* capacity caps, retry counts, precision levels
- *Feature selection:* algorithm variants, debug flags, mode switches
- *Numeric configuration:* SIMD widths, stride lengths, unroll factors
The compiler specializes code for each distinct value. It can remove dead
branches (`comptime if`), unroll loops (`comptime for`), replace inline
constants, and optimize aggressively with no runtime cost.
### Basic example
This function creates a fixed-size list initialized with a default value:
```mojo
comptime MyCollectionElement = ImplicitlyCopyable & Deinitable
def make_filled[T: MyCollectionElement, size: Int](
splat_value: T
) -> List[T]:
var result = List[T](capacity=size)
for _ in range(size):
result.append(splat_value)
return result^
```
The `size` parameter resolves at compile time. Each call site with a
different value gets its own specialized version:
```mojo
var three_zeros = make_filled[Int, 3](0)
var five_hellos = make_filled[String, 5]("hello")
print(three_zeros) # [0, 0, 0]
print(five_hellos) # [hello, hello, hello, hello, hello]
```
### Value parameters vs runtime arguments
Put values known at compile time in `[]`. Put values only known at
runtime in `()`.
Ask yourself: does the caller know this value when writing the code? If
yes, use a parameter. If it depends on input, files, or runtime state,
use an argument.
```mojo
# size is compile-time: the compiler specializes
def fixed[size: Int]():
var buf = Array[Int, size](fill=0)
# size is runtime: no specialization
def dynamic(size: Int):
var buf = List[Int](capacity=size)
```
## Parameterized types and explicit destruction {#generics-and-explicit-destruction}
Explicitly destroyed types don't always work with parameterized code. The issue
isn't parameterization; it's lifetime management. Explicit destruction gives you
control over teardown: you can define deinitializers that take arguments,
follow different paths, or raise errors.
Parameterized code that copies or moves values can't see or honor that logic.
Once a value is copied or transferred, you lose control over how (or
whether) it's cleaned up, and the code won't compile.
Watch for these cases:
- Parameterized code that manages lifetimes
- Parameterized code that copies or transfers values
These show up most often in containers, collections, and iterators that
copy values or take ownership and decide when destruction happens.
Safe cases are parameterized operations that don't affect lifetimes, such as
comparisons and predicates.
If your parameterized code needs to own, copy, or control when a value dies,
avoid explicitly destroyed types. Add a `Deinitable` constraint
to keep things working.
## Conditional trait conformance
:::caution
Certain capabilities may be unstable or unusable during roll-out. Use
caution with `RegisterPassable` and `TrivialRegisterPassable`.
:::
*Conditional trait conformance* uses checks before allowing a type to
adopt a trait. When the condition is satisfied, the type conforms. It
must fulfill the trait's requirements and gains any default implementation
the trait provides. When the condition isn't satisfied, Mojo skips the
conformance.
### Example: derived conformance
In the following declaration, Mojo conforms `Wrapper` to `Writable` when
its parameter `T` is also `Writable`:
```mojo
comptime BaseTraits = Copyable & Deinitable & Writable
@fieldwise_init
struct Wrapper[T: BaseTraits](
Writable where conforms_to(T, Writable)
):
var value: Self.T
```
When conforming to `Writable`, `Wrapper` doesn't need to implement any
methods. The trait provides a default implementation of `write_to()`.
Now consider a type that isn't `Writable`:
```mojo
@fieldwise_init
struct NotWritable(BaseTraits):
var data: Int
```
When instantiated with `Int` or `String` (both `Writable`), `Wrapper`
gains `Writable` conformance. With `NotWritable`, you can build the
struct, but you can't print it:
```mojo
var w_int = Wrapper[Int](42) # Int is Writable
print(w_int) # Wrapper[Int](value=42)
var w_str = Wrapper[String]("Hello") # String is Writable
print(w_str) # Wrapper[String](value=Hello)
# OK: only `Writable` conformance is unavailable
var w_not_writable = Wrapper[NotWritable](NotWritable(10))
# print(w_not_writable) # Compile-time error:
# Wrapper[NotWritable] doesn't conform to Writable
```
This pattern is standard for single-type containers like `Optional[T]`,
`Box[T]`, `Lazy[T]`, and `List[T]`. It says: "This type can do X if its
inner type can do X."
### Example: parts conformance
For types with multiple distinct components, the pattern extends
naturally. `Result[T, E]`, `Pair[L, R]`, `Dict[K, V]`, and similar
types say: "This type can do X if each of its parts can do X":
```mojo
comptime BaseTraits = Copyable & Deinitable
@fieldwise_init
struct Pair[L: BaseTraits, R: BaseTraits](
Hashable where conforms_to(L, Hashable) and conforms_to(R, Hashable)
):
var left: Self.L
var right: Self.R
@fieldwise_init
struct NotHashable(BaseTraits):
var data: Int
```
When both `L` and `R` are `Hashable`, the concrete `Pair` type becomes
`Hashable`, gaining the `hash()` method from the trait:
```mojo
var pair = Pair[Int, String](left=1, right="one")
var hash = hash(pair)
print(hash) # Prints the hash of the pair
# OK: only hashing is unavailable
var pair2 = Pair[Int, NotHashable](left=1, right=NotHashable(10))
# var hash2 = hash(pair2) # Compile-time error
```
### Example: conditional method access
When a type adopts a trait, it must satisfy the trait's required methods.
You gate those method implementations with `where` clauses, the same
conditions that control whether the conformance applies.
You can make `Wrapper` testable in `if` statements by conforming to
`Boolable`, which requires `__bool__()`:
```mojo
@fieldwise_init
struct Wrapper[T: BaseTraits](
Writable where conforms_to(T, Writable),
Boolable where conforms_to(T, Boolable),
):
var value: Self.T
def __bool__(self) -> Bool where conforms_to(Self.T, Boolable):
return self.value.__bool__()
```
The condition on `__bool__()` matches the one used for the `Boolable`
conditional conformance. Since the method and the conformance use the
same condition, they stay aligned. You won't end up with a conformance
but no method, or a method without the corresponding conformance.
```mojo
var w_str = Wrapper[String]("Hello")
if w_str: # Chooses the non-empty branch
print(t"Non-empty string \"{w_str.value}\" is truthy")
else:
print(t"Empty string \"{w_str.value}\" is falsy")
var w_empty_str = Wrapper[String]("")
if w_empty_str: # Chooses the empty branch
print(t"Non-empty string \"{w_empty_str.value}\" is truthy")
else:
print(t"Empty string \"{w_empty_str.value}\" is falsy")
```
Because `NotWritable` isn't `Boolable`, the condition on `__bool__()`
fails and the method isn't available:
```mojo
@fieldwise_init
struct NotWritable(BaseTraits):
var data: Int
var w_not_writable = Wrapper[NotWritable](NotWritable(10))
# Compile-time error: the method condition is false
if w_not_writable:
print(t"NotWritable with data {w_not_writable.value.data} is truthy")
else:
print(t"NotWritable with data {w_not_writable.value.data} is falsy")
```
`where` clauses on methods are useful beyond trait conformance too. You
can gate a method so it only works with non-empty lists, real numbers, or
values that fall within a collection's index bounds.
### Conditional trait composition
Mojo supports flexible condition composition:
- **Unconditional** — no special clauses:
```mojo
struct Foo(Copyable, Deinitable):
```
- **Simple condition** — as shown in `Wrapper`:
```mojo
struct Wrapper[T: BaseTraits](
Writable where conforms_to(T, Writable)
):
```
- **Hybrid** — mixes unconditional and conditional traits:
```mojo
struct Foo[T: AnyType](
Copyable, Writable where conforms_to(T, Writable)
)
```
- **Multiple aligned conditions** — as shown in `Pair`:
```mojo
struct Pair[L: BaseTraits, R: BaseTraits](
Hashable where conforms_to(L, Hashable) and conforms_to(R, Hashable)
):
```
- **Multiple independent conditions**:
```mojo
struct Foo[T: AnyType](
Writable where conforms_to(T, Writable),
Hashable where conforms_to(T, Hashable),
)
```
### Conditional conformance with value parameters
Conditional conformance also works with value parameters. You can gate
both conformance and methods on compile-time value conditions.
This is useful when a type's capabilities depend on a numeric parameter.
For example, a fixed-capacity wrapper might only be `Writable` when it
has capacity and its elements are writable. The `Sized` conformance is
unconditional:
```mojo
comptime ElementTraits = Writable & Copyable & Deinitable
struct SizedListWrapper[capacity: Int, T: ElementTraits](
Sized, Writable where conforms_to(T, Writable) and capacity > 0
):
var data: List[Self.T]
def __init__(out self, value: Self.T):
self.data = List[Self.T](capacity=Self.capacity)
for _ in range(Self.capacity):
self.data.append(value.copy())
def __len__(self) -> Int:
return len(self.data)
def write_to(self, mut writer: Some[Writer]):
writer.write(repr(self.data))
```
You can gate methods on value conditions:
```mojo
def first(self) -> Self.T where Self.capacity > 0:
return self.data[0].copy()
```
When `capacity` is one or more, the type conforms to `Writable` and
`first()` is available. When it's zero or less, neither is usable:
```mojo
var s = SizedListWrapper[5, Int](42)
print(s) # List of 42s
print(s.first()) # 42
# var s = SizedListWrapper[0, Int](42)
# print(s) # Error: Writable not satisfied
# print(s.first()) # Error: constraint is false
```
Value conditions follow the same rules as type conditions. You can
combine them with `and` and mix them with `conforms_to` checks in the
same `where` clause.
---
## Get started with Mojo
Get started with Mojo by building
[Conway's Game of
Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life),
a simulation in which cells live, die, and reproduce based on the state of
their neighbors.
This tutorial walks you through the steps to build a simple version of the
game. It should take you about 45-60 minutes to complete. Don't feel you
need to rush it.
Whether you arrive from, say, C++ or Python, you'll encounter unfamiliar
syntax like transfer operators and compile-time variables. Read the
*Takeaways* items to map these new features to concepts you already know.
Run the code in your terminal and watch the grid evolve over time. As you
build the game, you'll learn the Mojo syntax you need to start writing
programs of your own.
:::tip Before you start
Make sure you've [installed Mojo](/install) and can build and run Mojo code.
If you're using an AI coding assistant, install [Mojo agent
skills](/docs/tools/skills). The skills track current Mojo syntax and
language features.
```bash
npx skills add modular/skills
```
:::
## Game state
Conway's Game of Life runs on a two-dimensional grid. Each cell is either
alive or inactive. You'll use `1` for live cells and `0` for inactive
cells. Count the neighbors around each cell by adding their values to a
running sum.
Create `life.mojo`. Build an 8 x 8 grid containing a
[glider](https://en.wikipedia.org/wiki/Glider_(Conway%27s_Game_of_Life)):
```mojo
def main():
var count: Int = 64
var num_cols: Int = 8
var glider_grid = List[Int](length=count, fill=0)
# Convert (x, y) coordinates to a linear index in the grid
var to_index = lambda (x: Int, y: Int) -> Int: y * num_cols + x
# Set up the grid
for coord in [(0, 1), (1, 2), (2, 0), (2, 1), (2, 2)]:
glider_grid[to_index(coord[0], coord[1])] = 1
# Print the grid
for index in range(count):
print("X" if glider_grid[index] else ".", end="")
if index % num_cols == (num_cols - 1): print()
```
Run the program to see the initial glider configuration:
```bash
mojo life.mojo
```
Output:
```output
..X.....
X.X.....
.XX.....
........
........
........
........
........
```
### Takeaways
- All variable declarations like `count` and `num_cols` start with `var`.
- `var` bindings are mutable by default.
- The `to_index` *lambda expression* defines a small local function that
converts coordinates to a list index. Lambdas are short anonymous
functions that evaluate a single expression.
:::tip Worth knowing
When assigning contents, a list expression sets the values:
```mojo
var values: List[Int] = [12, -7, 64] # This is a list expression
```
In type names, square brackets supply compile-time *parameters*:
```mojo
List[Int] # List is a standard library-supplied type
Grid[8, 8] # Grid is a custom type
```
Parentheses supply run-time *arguments*:
```mojo
print(value)
Grid[8, 8]()
```
:::
## Add reusable printing
Move the display loop to a reusable function. Place this above `main()`:
```mojo
def print_grid(grid: List[Int], num_cols: Int):
for index in range(len(grid)):
print("X" if grid[index] else ".", end="")
if index % num_cols == (num_cols - 1): print()
```
Replace the print loop with `print_grid(glider_grid, num_cols)` and run.
Improve performance by replacing individual prints with a single string.
This solution uses *comprehensions*:
```mojo
def print_grid(self):
var grid_str = "".join(
[(("X" if value else ".") +
("\n" if (index + 1) % Self.num_cols == 0 else ""))
for index, value in enumerate(self.cells)
]
)
print(grid_str)
```
When running the program with larger grids and many generations, this
approach is faster than printing each cell individually. You print once per
frame instead of once per cell.
Call `print_grid()` from `main()` and confirm the output:
```mojo
# Print the grid
print_grid(glider_grid, num_cols)
```
### Takeaways
- Comprehensions are a shorthand for creating lists and other
collections.`print_grid()` uses a list comprehension to transform each
cell into a `String`, then joins them (`join()`) for printing.
- Enumeration (`enumerate()`) accesses each cell's index and value,
selecting "X" for live cells or "." for inactive cells. It also adds
newlines between rows.
## Add lookups
Make a few more changes in place in `life.mojo`:
```mojo
comptime count: Int = 64
comptime num_cols: Int = 8
# Transform coordinates to a linear index
comptime to_index = lambda (x: Int, y: Int) -> Int: (
y * num_cols + x
)
# Transform linear index to coordinates
comptime to_coord = lambda (i: Int) -> Tuple[Int, Int]: (
(i % num_cols, i // num_cols) # `//` is flooring divide
)
def print_grid(grid: List[Int]):
for index in range(len(grid)):
print("X" if grid[index] else ".", end="")
if index % num_cols == (num_cols - 1): print()
def main():
var glider_grid: List[Int] = List[Int](length=count, fill=0)
# Set up the grid
for coord in [(0, 1), (1, 2), (2, 0), (2, 1), (2, 2)]:
glider_grid[to_index(coord[0], coord[1])] = 1
# Print the grid
print_grid(glider_grid)
```
Now you can convert coordinates to indices and indices to coordinates.
### Takeaways
- `comptime` declarations are evaluated at compile time before the code
runs. This lets Mojo optimize and generate efficient machine code.
- The flooring divide operator (`//`) used here performs integer
division, rounding towards negative infinity. For integer types, `/`
returns an integer, rounding towards zero.
- You've moved all the constants out from `main()` to `comptime`
declarations, making them available throughout this file.
- You've created two lambda expressions that convert between
coordinates and indices.
- You've removed the second argument from `print_grid()`.
### Try this
In a separate source file, declare the number of rows as a comptime
constant and compute the *count* at compile time.
Remove the new source after verifying your solution is correct. You will
always need two of these three items: the number of rows, the number of
columns, and the total count of cells.
## Define a `Grid` type
Revise your code again, into a new file called `grid.mojo`. You're
creating a new type, called a `struct`:
```mojo
struct Grid[num_cols: Int, num_rows: Int]:
var cells: List[Int]
var count: Int
def __init__(out self):
self.count = Self.num_cols * Self.num_rows
self.cells = List[Int](length=self.count, fill=0)
def main():
var glider_grid = Grid[8, 8]()
```
`__init__(out self)` initializes a new `Grid`. Every field must receive a
value before the initializer returns.
Here, `count` comes from the grid dimensions, and `cells` starts as a list
of zeros.
### Takeaways
- `Grid` uses both compile-time parameters and runtime fields.
- `Self.num_cols` and `Self.num_rows` belong to the parameterized type.
- `self.cells` and `self.count` belong to one `Grid` instance.
- This `Grid` needs custom initialization, so it defines `__init__()`.
:::tip Worth knowing
Normally, if an initializer just assigns arguments directly to fields,
add `@fieldwise_init` to the struct instead. Mojo generates that
initializer for you. For example, in an alternate implementation
you might define all three core measurements as fields in your
struct rather than synthesize one from the other two:
```mojo
@fieldwise_init
struct Grid:
var cells: List[Int]
var count: Int
var num_cols: Int
var num_rows: Int
```
:::
## Add grid operations
Put the following content into your `Grid` struct:
```mojo
comptime to_index = lambda (x: Int, y: Int) -> Int: (
y * Self.num_cols + x
)
comptime to_coord = lambda (i: Int) -> Tuple[Int, Int]: (
(i % Self.num_cols, i // Self.num_cols)
)
def print_grid(self):
var grid_str = ""
for index in range(len(self.cells)):
grid_str += "X" if self.cells[index] else "."
if (index % Self.num_cols == (Self.num_cols - 1) and
index != len(self.cells) - 1):
grid_str += "\n"
print(grid_str)
def __setitem__(mut self, coord: Tuple[Int, Int], value: Int):
self.cells[Self.to_index(coord[0], coord[1])] = value
def __getitem__(self, coord: Tuple[Int, Int]) -> Int:
return self.cells[Self.to_index(coord[0], coord[1])]
```
### Takeaways
- `Self.to_index()` accesses a type member. The lambda `to_index()` belongs
to `Grid`. Uppercase `Self` refers to the type.
- Instance methods take `self` as their first argument. Lowercase `self`
refers to an instance.
- `mut self` allows a method to change the instance.
- `__getitem__()` and `__setitem__()` define indexing behavior, so a `Grid`
can use `grid[coord]` notation.
Don't add this anywhere. It's just a preview of how this all works from the
call site:
```mojo
# Set up the grid
for coord in [(0, 1), (1, 2), (2, 0), (2, 1), (2, 2)]:
glider_grid[coord] = 1 # Uses indexing with `__setitem__()`
```
## Import `Grid`
Remove `main()` from `grid.mojo`, save it, and import your new type into
`life.mojo`:
```mojo
from grid import Grid # Separate concerns into separate files
def main():
var glider_grid = Grid[8, 8]()
# Set up the grid
for coord in [(0, 1), (1, 2), (2, 0), (2, 1), (2, 2)]:
glider_grid[coord] = 1
# Print the grid
glider_grid.print_grid()
```
Run it and confirm everything works as expected.
## Be random
Gliders are terrific for validating code, but random values produce
great animations. Set up your seed in `main()`:
```mojo
from std.random import seed
from grid import Grid
def main():
seed()
# ...
```
Mojo won't allow runtime statements at global scope, so you can't call
`seed()` there. Instead, seed the RNG from executable code, such as
`main()` or another function. You only need to seed once: seeding sets the
state of a single PRNG shared across threads.
### Add protection
Filling every cell independently gives you random static. It works, but it
doesn't produce especially interesting Game of Life patterns. Instead,
build a few random clumps of live cells.
To start, add checks to your setter in `grid.mojo`. A coordinate check
function helps:
```mojo
def is_valid_coord(self, coord: Tuple[Int, Int]) -> Bool:
return not (
coord[0] < 0 or coord[0] >= Self.num_cols or
coord[1] < 0 or coord[1] >= Self.num_rows
)
def __setitem__(mut self, coord: Tuple[Int, Int], value: Int):
if not self.is_valid_coord(coord): return # no op
self.cells[Self.to_index(coord[0], coord[1])] = value
```
You can add a check to the getter, too. Instead of returning a made-up
value, raise an error or abort the process. The best approach tests the
coordinates before indexing (`if self.is_valid_coord(coord):`) to
avoid indexing errors instead of adding no-op workarounds.
### Introducing errors
This version of `__getitem__()` raises an error when coordinates aren't
valid. Add `raises` to the signature before the arrow, and call `raise`:
```mojo
def __getitem__(self, coord: Tuple[Int, Int]) raises -> Int:
if not self.is_valid_coord(coord):
raise String(t"Invalid coordinate: ({coord[0]}, {coord[1]})")
return self.cells[Self.to_index(coord[0], coord[1])]
```
A *TString* starts with `t"` and creates a template format. `print()`
statements automatically convert to strings, but everywhere else explicitly
call `String()`.
Once `__getitem__()` raises, every caller must either raise or handle
the error using Mojo's `try/except` error handling.
For now, revert your changes and let `__getitem__()` handle invalid
coordinates as it did before.
### Construct the random grid
Add a static method that constructs a random `Grid`:
```mojo
from std.random import random_si64 # Add this import to grid.mojo
# and inside Grid:
@staticmethod
def random_grid(clumps: Int = 2) -> Self:
var grid = Self()
for _ in range(clumps):
var idx = Int(random_si64(0, Int64(grid.count) - 1))
var x, y = Self.to_coord(idx)
grid[(x, y)] = 1 # Fill index cell
for dx in range(-1, 2): # -1, 0, or 1
for dy in range(-1, 2):
if not grid.is_valid_coord((x + dx, y + dy)): continue
if random_si64(0, 3) > 0: continue # 75% skip
grid[(x + dx, y + dy)] = 1
return grid^
```
By default, `random_grid()` builds two clumps when called without arguments
(`clumps: Int = 2`). The default value follows the equal sign.
A `@staticmethod` belongs to the type and not an individual instance. Call
it through the `Grid` type:
```mojo
print("\nRandom grid:\n")
var random_grid = Grid[8, 8].random_grid()
random_grid.print_grid()
```
You don't have to add this code except to test it. You can remove it
afterwards.
### Takeaways
- This static method still needs `Grid` parameters, namely 8 and 8.
- Each neighboring cell has a 25% chance of becoming live. Adjust
to your preference.
- `random_si64()` returns an `Int64`. Cast the value to `Int`.
- The clump loop variable is `_`, the discard pattern. Use this when
you don't care about the value it produces.
- A `^` sigil transfers the newly initialized `Grid`, avoiding a copy.
Transfer means changing ownership, handing the value to the new owner.
By skipping a copy, you avoid the time and memory costs of duplicating
data.
## Evolve
In `life.mojo`, cut out the glider grid and remove the print statements.
Next, you'll start making the grid change over time. Start by adding a
separate `next_cells` list to `Grid` and initialize it:
```mojo
struct Grid[num_cols: Int, num_rows: Int]:
var cells: List[Int] # Holds the current generation
var next_cells: List[Int] # Holds the next generation
var count: Int
def __init__(out self):
self.count = Self.num_cols * Self.num_rows
self.cells = List[Int](length=self.count, fill=0)
self.next_cells = List[Int](length=self.count, fill=0)
```
Conway's Game of Life applies three rules to every cell:
- A live cell stays alive with two or three live neighbors.
- An inactive cell becomes alive with exactly three live neighbors.
- Every other cell is inactive in the next generation.
The updated cells are stored in the `next_cells` list. They won't affect
math for the previous generation.
Each cell has eight neighbors: the cells in the 3 x 3 square around it,
excluding the cell itself.
Add these methods to `Grid`.
`evolve_cell()` determines the state for the next generation of a single
cell based on its neighbors:
```mojo
def evolve_cell(mut self, i: Int):
var is_live = Bool(self.cells[i])
self.next_cells[i] = 0
# Count the neighbors
var ncount = -1 if is_live else 0 # Exclude self from the count
var x, y = Self.to_coord(i)
for dx in range(-1, 2):
for dy in range(-1, 2):
var nx = x + dx
var ny = y + dy
ncount += self.cells[Self.to_index(nx, ny)]
# Live cell stays alive with two or three live neighbors
if is_live and (ncount == 2 or ncount == 3):
self.next_cells[i] = 1
# Inactive cell becomes alive with exactly three live neighbors
elif not is_live and ncount == 3:
self.next_cells[i] = 1
```
`evolve()` updates the entire grid to the next generation by calling
`evolve_cell()` for each index:
```mojo
def evolve(mut self):
for i in range(self.count):
var x, y = Self.to_coord(i)
# Edges are excluded from evolution and will always go inactive.
if (x == 0 or y == 0 or
x == Self.num_cols - 1 or y == Self.num_rows - 1):
self.next_cells[i] = 0 # Edges go inactive
continue
self.evolve_cell(i)
# Swap the current and next cell states
var tmp = self.cells^ # Transfer
self.cells = self.next_cells^ # Transfer
self.next_cells = tmp^ # Every cell is written, so this is safe
```
### Takeaways
- The algorithm progresses in integer order, excluding edges.
- Live cells offset by -1, to exclude them from the count.
- Mojo's "ternary" has no `? :` syntax. Use the Python-style `if-else`
expression instead: `-1 if is_live else 0`.
## Run the simulation
Add a loop so you can watch the grid evolve in the terminal. Here's the
final `life.mojo`:
```mojo
from std.random import seed
from grid import Grid
from std.time import sleep
def main():
comptime gridw: Int = 80
comptime gridh: Int = 20
comptime grid_count: Int = 400
seed()
var grid = Grid[gridw, gridh].random_grid(grid_count)
while True:
for gen in range(100):
print(t"\033[H\033[J\nGeneration: {gen}{' ' * 4}")
grid.evolve(); grid.print_grid()
sleep(0.1)
grid = Grid[gridw, gridh].random_grid(grid_count)
```
### Takeaways
- `comptime` declarations let you set constants.
- The odd characters in the print statement are ANSI escape codes.
You'll see the updates generation-by-generation. Keep your terminal
at a minimum of 80x24 for best results.
:::tip Worth knowing
Mojo uses semicolons to separate statements, not to end them. One
statement per line is the usual form. This listing pairs them to stay
compact.
:::
- Mojo uses semicolons to separate statements, not to end them. One
statement per line is the usual form. This listing pairs them to stay
compact.
## Your first day? Try these
- To use AI coding assistants with Mojo, see [our AI skills
guide](/docs/tools/skills/) for using the latest
up-to-date language know-how.
- Our Mojo [language
reference](/docs/reference/) section provides a
concise reference for syntax, keywords, and more.
- You can download our [cheat
sheets](/docs/reference/cheat-sheets/) for printable
reference cards that unify entire concepts.
- [Mojo Quest](https://quest.mojolang.org/) is a web-based game where you
solve coding challenges to practice Mojo syntax.
## Final code
View the complete grid.mojo
```mojo
from std.random import random_si64
struct Grid[num_cols: Int, num_rows: Int]:
var cells: List[Int] # Holds the current generation
var next_cells: List[Int] # Holds the next generation
var count: Int
def __init__(out self):
self.count = Self.num_cols * Self.num_rows
self.cells = List[Int](length=self.count, fill=0)
self.next_cells = List[Int](length=self.count, fill=0)
comptime to_index = lambda (x: Int, y: Int) -> Int: (
y * Self.num_cols + x
)
comptime to_coord = lambda (i: Int) -> Tuple[Int, Int]: (
(i % Self.num_cols, i // Self.num_cols)
)
def print_grid(self):
var grid_str = "".join(
[(("X" if value else ".") +
("\n" if (index + 1) % Self.num_cols == 0 else ""))
for index, value in enumerate(self.cells)
]
)
print(grid_str)
def is_valid_coord(self, coord: Tuple[Int, Int]) -> Bool:
return not (
coord[0] < 0 or coord[0] >= Self.num_cols or
coord[1] < 0 or coord[1] >= Self.num_rows
)
def __setitem__(mut self, coord: Tuple[Int, Int], value: Int):
if not self.is_valid_coord(coord):
return # no op
self.cells[Self.to_index(coord[0], coord[1])] = value
def __getitem__(self, coord: Tuple[Int, Int]) -> Int:
return self.cells[Self.to_index(coord[0], coord[1])]
@staticmethod
def random_grid(clumps: Int = 2) -> Self:
var grid = Self()
for _ in range(clumps):
var idx = Int(random_si64(0, Int64(grid.count) - 1))
var x, y = Self.to_coord(idx)
grid[(x, y)] = 1 # Fill index cell
for dx in range(-1, 2): # -1, 0, or 1
for dy in range(-1, 2):
if not grid.is_valid_coord((x + dx, y + dy)):
continue
if random_si64(0, 3) > 0:
continue # 75% skip
grid[(x + dx, y + dy)] = 1
return grid^
def evolve_cell(mut self, i: Int):
var is_live = Bool(self.cells[i])
self.next_cells[i] = 0
# Count the neighbors
var ncount = -1 if is_live else 0 # Exclude self from the count
var x, y = Self.to_coord(i)
for dx in range(-1, 2):
for dy in range(-1, 2):
var nx = x + dx
var ny = y + dy
ncount += self.cells[Self.to_index(nx, ny)]
# Live cell stays alive with two or three live neighbors
if is_live and (ncount == 2 or ncount == 3):
self.next_cells[i] = 1
# Inactive cell becomes alive with exactly three live neighbors
elif not is_live and ncount == 3:
self.next_cells[i] = 1
def evolve(mut self):
for i in range(self.count):
var x, y = Self.to_coord(i)
# Edges are excluded from evolution and will always go inactive.
if (
x == 0
or y == 0
or x == Self.num_cols - 1
or y == Self.num_rows - 1
):
self.next_cells[i] = 0 # Edges go inactive
continue
self.evolve_cell(i)
# Swap the current and next cell states
var tmp = self.cells^ # Transfer
self.cells = self.next_cells^ # Transfer
self.next_cells = tmp^ # Every cell is written, so this is safe
```
View the complete life.mojo
```mojo
from std.random import seed
from grid import Grid
from std.time import sleep
def main():
comptime gridw: Int = 80
comptime gridh: Int = 20
comptime grid_count: Int = 400
seed()
var grid = Grid[gridw, gridh].random_grid(grid_count)
while True:
for gen in range(100):
print(t"\033[H\033[J\nGeneration: {gen}{' ' * 4}")
grid.evolve()
grid.print_grid()
sleep(0.1)
grid = Grid[gridw, gridh].random_grid(grid_count)
```
---
## Mojo Manual
Welcome to the Mojo Manual, the authoritative learning path for Mojo.
This manual is for developers and researchers who know how to program and
want to build with Mojo.
The Mojo Manual gets you working quickly, then goes deeper into the ideas
that give Mojo its range. You'll move from core Mojo into its programming
model, compile-time features, and direct control of values, memory, and
hardware.
Ready to write some code?
- [Quickstart](/docs/manual/quickstart/) gives you a fast tour of Mojo
syntax and other fundamentals while confirming that your toolchain is
set up and working.
- [Build Conway's Game of Life](/docs/manual/get-started) for a bigger
project that walks you through creating a complete Mojo command-line
application.
As you read, keep these references handy:
- [The Mojo language reference](/docs/reference/) provides quick lookups
for syntax, keywords, and more.
- [Cheat sheets](/docs/reference/cheat-sheets/) bring entire concepts
together in printable, visual reference cards.
---
## Automatic destruction
Mojo destroys values as soon as they're no longer used. It doesn't wait for
the end of a code block or even the end of an expression. With Mojo's
*as-soon-as-possible* (ASAP) destruction policy, intermediate values in an
expression such as `a + (b - c) * d` can be destroyed as soon as their last
use completes.
At compile time, Mojo determines the last use of each value. After its last
use, the value's lifetime ends and Mojo calls its `__deinit__()`
deinitializer.
For cleanup that must happen at a specific, compiler-checked point, Mojo
also supports *explicit* deinitializers. See
[Explicit value destruction](/docs/manual/lifecycle/explicit-destroy/).
## When Mojo destroys values
Track when each Number instance is deinitialized by overloading `__deinit__()`
to print a message:
```mojo
@fieldwise_init
struct Number(Writable):
var value: Int
# Track the destruction of each Number instance
def __deinit__(deinit self):
print(t"Destroying Number(value={self.value})")
# Add two Number values together
def __add__(self, other: Number) -> Number:
return Number(self.value + other.value)
# Subtract a Number value from another
def __sub__(self, other: Number) -> Number:
return Number(self.value - other.value)
# Multiply two Number values together
def __mul__(self, other: Number) -> Number:
return Number(self.value * other.value)
def main():
var a = Number(1)
var b = Number(2)
var c = Number(3)
var d = Number(4)
# 1 2 3 4
print(a + (b - c) * d)
# The output shows the order of destruction for each value.
# Expression precedence determines the order of evaluation, and
# therefore the order in which these last uses occur:
# Destroying Number(value=3) # PARENTHESIZED SUBTRACTION FIRST
# Destroying Number(value=2) # PARENTHESIZED SUBTRACTION FIRST
# Destroying Number(value=-1) # MULTIPLICATION SECOND, intermediate value
# Destroying Number(value=4) # MULTIPLICATION SECOND
# Destroying Number(value=-4) # ADDITION THIRD, intermediate value
# Destroying Number(value=1) # ADDITION THIRD
# Number(value=-3) # PRINTS RESULT
# Destroying Number(value=-3) # RESULT IS DESTROYED; DEINITIALIZER RUNS
```
Every value is initialized once and deinitialized once. This happens as the
expression is evaluated in precedence order: the parenthesized subtraction
first, then multiplication, then addition. Values are destroyed as soon as
their last use completes.
Intermediate values that aren't bound to variables (-1, -4, and -3 in this
example) also have lifecycles. They're destroyed after their last use.
## Deinitializer behavior
`__deinit__()` uses the `deinit`
[argument convention](/docs/manual/values/ownership/#argument-conventions)
for `self`. The value and its fields remain valid while `__deinit__()`
performs cleanup. When the method returns, the instance becomes logically
deinitialized.
Mojo generates a `__deinit__()` for every struct with deinitializable fields.
In general, don't call `__deinit__()` directly. If you need that level of
control, use [explicit value
destruction](/docs/manual/lifecycle/explicit-destroy/). You may also need
to call `__deinit__()` directly when wrapping a deinitializer for
parameterized types.
## Moving values out of fields
Deinitializers are the only place where you can safely move values out of an
instance's fields without having to reinitialize the field before its
next use.
This special rule isn't about where the code lives. It's about
guarantees:
```mojo
def __deinit__(deinit self):
var name = self.name^ # OK: can take ownership of fields
```
The compiler knows that deinitializers like `__deinit__()` are the final
use of an instance. Because of this, it allows you to move values out of
fields without reinitializing them.
This ensures that fields are only moved out of struct instances when the
compiler can guarantee the instance is at the end of its lifetime.
Like instances, struct fields use ASAP destruction within `__deinit__()`
methods. For example:
```mojo
struct S:
var a: String
var b: String
def __deinit__(deinit self):
# Mojo calls a.__deinit__() here.
use(b)
# Mojo calls b.__deinit__() here.
```
## Custom deinitializers
Define a custom `__deinit__()` when your type needs to perform cleanup as
it's destroyed. For example, you might free manually allocated memory or
close a long-lived resource such as a file.
This logger owns a temporary file. Its deinitializer closes the file when
the logger is destroyed:
```mojo
from std.tempfile import NamedTemporaryFile
struct QuickLogger:
var temporary_file: NamedTemporaryFile
def __init__(out self) raises:
self.temporary_file = NamedTemporaryFile(mode="w", delete=False)
# Log a message to the temporary file
def log(mut self, message: String) raises:
self.temporary_file.write(message + "\n")
def __deinit__(deinit self):
try:
print(t"Closing: {self.temporary_file.name}")
self.temporary_file.close()
except e:
print(t"Error: {e}")
def main() raises:
var ql = QuickLogger()
ql.log("This is a test log message.")
ql.log("This is the last use of 'ql'")
```
## Explicit lifetime extension
Most of the time, Mojo's ASAP destruction requires no extra effort. You may
need to explicitly mark the last use of a value to control when its
deinitializer runs.
Use explicit lifetime extension when something outside the value's ordinary
uses still requires it to remain alive and you can't use origins or
references to do this.
Assign the value to the `_` *discard pattern* where you want its lifetime to
end. This marks its last use, so the deinitializer runs immediately after
the statement:
```mojo
var s = "abc"
print(s) # s.__deinit__() runs after this line
# Extend t's lifetime to the discard line
var t = "xyz"
print(t)
# ... some time later
_ = t # t.__deinit__() runs after this line
```
Two cases particularly need explicit lifetime extension: an RAII guard, such
as a lock, that would otherwise be released too early, and a pointer whose
origin has been erased, which can lead to a use-after-free. Neither produces
a compiler error. `_ = value` keeps the value alive until the point where you
need it released or need the pointer to remain valid.
:::note
Previous versions of Mojo required the transfer sigil (`^`) when
discarding a move-only type. This is no longer required, since the
compiler doesn't move the discarded value. For more on the
transfer sigil, see
[ownership
transfer](/docs/manual/values/ownership/#transfer-arguments-var-and-).
:::
---
## Explicit destruction
Normally, Mojo automatically destroys a value after its last use. Explicit
destruction lets a type opt out of that behavior and require its values to
be deliberately destroyed. A type that does this is an explicitly destroyed
type.
Explicit destruction uses an unsatisfiable `Deinitable` constraint.
Mark types with a `Deinitable where False` conditional conformance:
```mojo
@fieldwise_init
struct Example(Deinitable where (False, "call 'cleanup()'")):
def cleanup(deinit self):
# perform cleanup operations
pass
def main():
var value = Example()
# use value
value^.cleanup()
```
Provide named deinitializer methods that use the `deinit self` argument
convention. Make the error message actionable and follow Mojo best practices:
use lower case for `call`, put method and function names in single quotes,
include the parentheses, and omit the final period.
Values that require explicit destruction must be consumed by a named
deinitializer or transferred out of scope. Otherwise, the compiler reports
an error.
## When to use explicit destruction
Choose explicit destruction when cleanup must be controlled in code. Common
cases include:
- Cleanup can fail and requires error handling.
- Multiple cleanup paths are possible.
- The order of cleanup operations matters.
- Cleanup is expensive and should be deliberate.
Examples:
```mojo
# Multiple cleanup paths
@fieldwise_init
struct Transaction(
Deinitable where (False, "call 'commit()' or 'rollback()'")
):
def commit(deinit self) raises: # Offers error handling
# ...
pass
def rollback(deinit self):
# ...
pass
# Order matters
struct MutexGuard(Deinitable where False):
# Must be called to release the lock before other operations
def unlock(deinit self):
# ...
pass
```
### Raising deinitializers
`__deinit__()` can't raise; the compiler rejects the `raises` keyword on it.
A named deinitializer can raise, which lets cleanup report failure to its
caller.
Because `deinit self` consumes the value at the call, the binding is consumed
whether the call returns or raises. The caller can't invoke another
deinitializer on that same instance.
If the type contains explicitly destroyed fields, the deinitializer must
dispose of them before it raises. Implicitly destructible fields are cleaned
up during unwinding.
## Custom error messages
Add a custom message to the `where` constraint to improve compiler
diagnostics:
```mojo
struct CustomFileHandle(
Deinitable where (False, "call 'save_and_close()' or 'discard()'")
):
def save_and_close(deinit self) raises:
pass
def discard(deinit self):
pass
```
## Parameterized code and explicit destruction
Parameterized code using broad constraints like `AnyType` and `Movable` can
accept both implicitly and explicitly destroyed values. However, that code
*can't* destroy values that require explicit destruction except under
specific circumstances.
These are the key patterns:
```mojo
# Error if T requires explicit destruction
def owning_function[T: AnyType](var value: T):
pass # Error: value abandoned here
# Constrain T to Deinitable when implicit destruction is acceptable
def deinitable_function[T: Deinitable](var value: T):
pass # value.__deinit__() called automatically
# Return the value so the caller keeps destruction responsibility
def pass_through_function[T: Movable](var value: T) -> T:
return value^
# Accept a deinitializer so the function can destroy the value
def consuming_function[
T: Movable, //, Cleanup: def(var T)
](var value: T, consume: Cleanup):
consume(value^) # deinitialize here
```
### Example types for parameterized destruction
The following examples use two types with different destruction requirements:
`Basic` is destroyed implicitly, while `Tally` requires explicit destruction.
```mojo
from std.memory import Allocation, Layout, alloc, dealloc
@fieldwise_init
struct Basic(Movable):
var string: String
def __deinit__(deinit self):
print(t"Destroying Basic: {self.string}")
struct Tally(Movable, Deinitable where (
False,
"call 'destroy()' to free the counters"
)):
var counts: Allocation[Int64]
def __init__(out self, buckets: Int):
self.counts = alloc(Layout[Int64](count=buckets))
self.counts.unsafe_span().fill(0)
def record(mut self, bucket: Int):
self.counts.unsafe_span()[bucket] += 1
def destroy(deinit self):
print(t"Destroying Tally: {self.counts.unsafe_span()}")
dealloc(self.counts^)
```
### Parametric pass-through
A parameterized pass-through function doesn't need to know its value's
destruction model. The caller retains responsibility for ending the value's
lifetime:
```mojo
def pass_through[T: Movable](var value: T) -> T:
# perform work with value
return value^
```
Since the function doesn't maintain value ownership, it doesn't need to
know *how* to destroy it:
```mojo
var tally = Tally(3)
tally.record(2)
tally = pass_through(tally^)
tally^.destroy() # Destroying Tally: [0, 0, 1]
var basic = Basic("Hello")
_ = pass_through(basic^) # Destroying Basic: Hello
```
### Parametric consumption: Movable
Pass a deinitializer to a parameterized function so it knows how to consume
any `Movable` value. The caller may transfer the value or a copy, knowing
it won't be abandoned.
```mojo
def consuming_method[
T: Movable, //, Cleanup: def(var T)
](var value: T, consume: Cleanup):
print(t"Consuming: {reflect[T].name()}")
consume(value^)
```
Each explicitly destroyed type has an unknown set of deinitializer methods.
Parametric code can't anticipate what these are. Using a lambda helps you
pass a type-specific deinitializer to a consuming parametric function.
For example, a `Tally` value:
```mojo
var tally = Tally(2)
tally.record(1)
comptime tally_consumer = lambda (var t: Tally): t^.destroy()
consuming_method(tally^, tally_consumer)
# Output:
# Consuming: .Tally (consuming_method)
# Destroying Tally: [0, 1] (destroy())
```
### Parametric consumption: Deinitable
There's nothing to pass for all-Deinitable types that use no-argument
deinitializers:
```mojo
# Works across all Deinitable types
comptime implicit_consumer = lambda [T: Deinitable](
var value: T
): T.__deinit__(value^)
```
For example, a `Basic` value:
```mojo
var basic = Basic("World")
consuming_method(basic^, implicit_consumer[Basic])
# Output:
# Consuming: .Basic (from `consuming_method()`)
# Destroying Basic: World (from `__deinit__()`)
```
## Related
- [Value destruction](/docs/manual/lifecycle/death/) - Complete coverage of
value destruction and lifetime management
- [`AnyType`](/docs/std/traits/anytype/AnyType/) - Base trait for all types
- [`Deinitable`](/docs/std/traits/deinitable/Deinitable/) - Trait for
automatically deinitializable types
---
## Value lifecycles
A Mojo value has a beginning and an end. Mojo creates and initializes the
value before you use it, then destroys it when it's no longer needed.
Lifecycle methods define what happens at each stage. Use them to control how
your types initialize their state, transfer values, and release resources.
---
## Initialization state
Mojo tracks two kinds of initialization for structs: *fieldwise* and
*logical*.
Fieldwise initialization means every field contains a valid value. Logical
initialization means the instance as a whole is valid and ready to use. A
struct needs both before you can use it.
## The basics
You create struct instances by calling the `__init__()` initializer:
```mojo
struct Person:
var name: String
var age: Int
def __init__(out self, name: String, age: Int):
self.name = name
self.age = age
def main():
var me = Person("Alice", 30)
```
Calling `Person("Alice", 30)` is syntactic sugar for calling the
initializer directly:
```mojo
var me: Person
me = Person.__init__("Alice", 30) # Identical
```
When constructing a Person, the compiler allocates the
necessary storage and `__init__()` initializes that memory.
## Fieldwise vs logical initialization
Initializing a struct by assigning values directly to its fields may
populate the data, but it doesn't make the instance usable:
```mojo
@fieldwise_init
struct Person(Writable):
var name: String
var age: Int
def main():
var me: Person
me.name = "Alice"
me.age = 25
print(me) # Error
# error: 'me' used with all fields manually initialized
# but without calling an '__init__' method
```
In this example, all fields contain valid values, but the instance is still
not considered initialized.
Assigning every field satisfies *fieldwise* initialization, but without
running an `__init__()` method, it doesn't satisfy *logical* initialization.
Construct the value with an initializer to establish both:
```mojo
var me: Person # Not initialized
me = Person("Alice", 30) # Logically and fieldwise initialized after call
print(me)
```
After `__init__()` completes, the instance is safe to use.
## Inside `__init__()`
Within `__init__()`, `self` is logically initialized, but its fields are
uninitialized. This reverses the situation before calling `__init__()`,
where the fields are initialized but `self` is not:
```mojo
def __init__(out self, name: String, age: Int):
# At this point:
# - Logically initialized (self is valid as an instance)
# - Fieldwise uninitialized (fields have no values yet)
self.name = name
self.age = age
# Now both logically and fieldwise initialized
```
Entering `__init__()` establishes the instance. It's your responsibility to
populate every field:
```mojo
def __init__(out self, name: String, age: Int):
self.name = name
# Error: field 'age' not initialized in __init__
```
The `__init__()` signature doesn't have to mirror the struct's fields.
You can use parameters, constants, or external values to initialize them:
```mojo
# Parameters can be used to initialize fields
self._store = List[T](capacity=Count)
# Constants can be used to initialize fields
self.string = ""
# External values can be used to initialize fields
from std.math import pi
self.default_angle = pi / 2.0
self.uuid = MyUUIDImplementation.uuid()
```
### Calling methods
You can't call methods until all fields are initialized:
```mojo
def __init__(out self, name: String):
self.greet() # Error: self not fully initialized
self.name = name
self.greet() # OK: all fields initialized
```
Field initialization is limited to `__init__()` methods.
Regular methods can't initialize individual fields of an `out` argument,
but `__init__()` methods can.
---
## Creating values
A value's life in Mojo begins when you construct it, directly or
implicitly. The type uses an *initializer* to prepare the value for use.
Each constructible type provides one or more initializer overloads.
Initializers set up the value's fields and perform any other required
preparation.
Initializers and deinitializers together define a value's lifecycle. This
page covers value creation and initialization.
## Initializers {#constructor}
For simple types, use `@fieldwise_init` to have Mojo generate an
initializer:
```mojo
@fieldwise_init
struct MyStruct:
var field1: Int
var field2: String
```
For more complex types, or when you need more control, write your own
initializer:
```mojo
struct MyStruct:
var field1: Int
var field2: String
def __init__(out self, field1: Int, field2: String):
self.field1 = field1
self.field2 = field2
```
An initializer must set up every field in a value. If any field is
uninitialized when the initializer finishes, the compiler reports an error.
All initializers use the `out self` argument convention. The initializer
constructs `self` rather than declaring or explicitly returning a result:
```mojo
# Works with both fieldwise initialization and
# hand-written initializers
var new_instance = MyStruct(1, "Hello")
```
Custom initializers can provide default values, calculate fields, validate
arguments, or initialize resources.
Where possible, Mojo automatically provides specialized initializers for
types that conform to `Copyable` or `Movable`.
### Overloading initializers
Like other methods, you can
[overload](/docs/manual/functions/#overloaded-functions) `__init__()` to
provide different ways to initialize a value.
Initializer overloads can delegate to each other and use default arguments.
For example:
```mojo
struct RetryPolicy:
var max_attempts: Int
var delay_ms: Int
# Factory-style convenience overload delegates to the core initializer
def __init__(out self):
self = Self(3) # Syntax sugar for `self.__init__(3)`
# Core initializer provides the default delay
def __init__(out self, max_attempts: Int, delay_ms: Int = 1000):
self.max_attempts = max_attempts
self.delay_ms = delay_ms
```
This provides several ways to construct the same type:
```mojo
var standard = RetryPolicy()
var persistent = RetryPolicy(10)
var aggressive = RetryPolicy(10, 250)
```
### Initializers and implicit conversion
Mojo can implicitly convert values when a different type is required during
assignment or when passing or returning a value.
For example, `Optional[T]` supports implicit conversion from `T` and `None`:
```mojo
var greeting: Optional[String] = None
greeting = String("Salve!")
```
Enable implicit conversion by marking an initializer with
[`@implicit`](/docs/reference/decorators/implicit/):
```mojo
struct Target:
@implicit
def __init__(out self, source: Source):
# ...
```
Use implicit conversions sparingly. They work best when the conversion is
safe, constant-time, and has one clear meaning. For example:
```mojo
struct Complex:
var real: Float64
var imag: Float64
def __init__(out self, real: Float64, imag: Float64):
self.real = real
self.imag = imag
@implicit
def __init__(out self, value: Float64):
self = Complex(value, 0.0)
def magnitude_squared(value: Complex) -> Float64:
return value.real * value.real + value.imag * value.imag
def main():
# Implicitly converts 1.6 to Complex(1.6, 0.0)
var complex: Complex = 1.6
# Implicitly converts 3.0 to Complex(3.0, 0.0) in call
var result = magnitude_squared(3.0)
```
### Initializer lists
Without an `@implicit` initializer, `Complex` would lose its implicit
conversion. *Initializer lists* provide another convenience: braced
construction of an expected type without spelling its name. This syntax
works whether the type provides implicit initialization or not:
```mojo
# Instead of these full type construction calls:
var result = magnitude_squared(Complex(real=3.0, imag=0.0)) # Full, keyword
var result = magnitude_squared(Complex(3.0, 0.0)) # Full, positional
var result = magnitude_squared(Complex(value=3.0)) # Convenience, keyword
var result = magnitude_squared(Complex(3.0)) # Convenience, positional
# With braced syntax:
var result = magnitude_squared({real=3.0, imag=0.0}) # Full, keyword
var result = magnitude_squared({3.0, 0.0}) # Full, positional
var result = magnitude_squared({value=3.0}) # Convenience, keyword
var result = magnitude_squared({3.0}) # Convenience, positional
```
This is useful for compiler-inferred parameterized types, whose full type
names can be long and verbose.
### No-initializer types
Mojo allows you to write types that can't be constructed. If a type
declares no initializer, you can't create an instance and there's no
lifecycle to manage.
Use them to host static content and behavior without state:
```mojo
struct HTTPStatus:
comptime OK = 200
comptime NOT_FOUND = 404
comptime INTERNAL_SERVER_ERROR = 500
@staticmethod
def is_success(code: Int) -> Bool:
# 2xx is the HTTP status success class
return 200 <= code < 300
```
For example:
```mojo
def handle(status_code: Int) -> String:
if HTTPStatus.is_success(status_code):
return "ok"
return "failed"
```
## Copy and move initializers
Copy and move initializers use another value of the same type:
```mojo
var the_copy = value.copy() # AKA ValueType(copy=value)
var the_move = value^ # AKA ValueType(move=value^)
```
### Copy initializer {#copy-constructor}
`Copyable` establishes values that can be copied. It provides the `copy()`
method and, when possible, Mojo synthesizes the required copy initializer:
```mojo
def __init__(out self, *, copy: Self):
# ...
```
A `Copyable` constraint lets generic code explicitly copy a value:
```mojo
def copy_return[T: Copyable](foo: T) -> T:
var copy = foo.copy()
return copy^
```
All `Copyable` types are also `Movable`, so you can transfer ownership of
the copy when returning it, as shown here.
### Implicitly-copyable types
`ImplicitlyCopyable` allows the compiler to insert copies where an explicit
copy would otherwise be required.
It refines `Copyable`, so conforming types also support `copy()` and the
copy initializer. Use `ImplicitlyCopyable` only when implicit copying is
required by the compiler or an API contract.
Implicit copying can hide potentially expensive work. Prefer an explicit
`copy()` call, especially when copying may allocate memory or otherwise have
significant cost. This keeps the operation visible at the call site.
### Move initializer {#move-constructor}
Consider the `RetryPolicy` type defined earlier on this page and the
following example that transfers ownership of a policy value to a new
variable:
```mojo
var policy = RetryPolicy(3, 1000)
var transferred = policy^
```
Although `RetryPolicy` declared no conformances, the transfer operator
still works here. Mojo synthesizes a move initializer for it.
Define custom move initializers when transferring requires custom behavior:
```mojo
def __init__(out self, *, deinit move: Self):
# ...
```
### Move-only and immovable types
A type that conforms to `Movable` but not `Copyable` is move-only. For
example, [`OwnedPointer`](/docs/std/memory/owned_pointer/OwnedPointer/) can
transfer ownership of its stored value but can't copy it.
[`Atomic`](/docs/std/atomic/atomic/Atomic/) is also move-only, preventing
copies that would duplicate its value.
A type conforming to neither `Movable` nor `Copyable` is immovable.
To opt out of `Movable`, conform your type to `Movable where False` or
define it with a non-movable field:
```mojo
struct Pinned(Movable where False):
var n: Int
def __init__(out self, n: Int):
self.n = n
```
Mojo rejects `Pinned` value transfers.
Immovable types are useful when a value must remain at a stable memory
address. For example:
- Other values hold pointers to it, making its address part of its identity.
Moving it would leave those pointers dangling.
- The value contains a pointer to itself. Moving its bits would leave the
interior pointer referring to the old, invalid address.
- An external system tracks the value by address while an operation is in
progress. Moving it would invalidate that association.
---
## Compile-time evaluation
To understand Mojo's metaprogramming, you need to understand how Mojo runs
code at compile time. Several things can trigger compile-time code execution:
- Assigning an expression to a `comptime` value.
- Evaluating a `comptime` conditional or loop.
- Assigning an expression to a compile-time parameter.
- And a few less common cases, all identified with the `comptime` keyword.
Here are some examples:
```mojo
comptime SIZE = 1024 // 32
```
Here the expression `1024 // 32` invokes the `IntLiteral.__floordiv__()`
method. Since it occurs in a `comptime` assignment, the method must be run
at compile time.
```mojo
comptime for i in range(4):
print(i)
```
Here the `range(4)` function needs to run to produce an iterator for the
`comptime for` statement.
```mojo
var array = Array[Int, get_array_size()]()
```
In this example, the `get_array_size()` function needs to run at compile
time to determine the `length` parameter, which forms part of the type of
`array`. (For example, if `get_array_size()` returns 32, the type of the
`array` variable is `Array[Int, 32]`.)
When the compiler encounters a function call in a compile-time context, the
compiler runs the function separately, as if it was a small separate
program. This is similar in concept to how C++ evaluates a `constexpr`.
(For a slightly deeper look at this process, see [How the compiler runs
code](#how-the-compiler-runs-code).)
While most code can run at compile time, Mojo won't run code that depends
on the execution environment. The following are examples of code that Mojo
won't run at compile time:
- File I/O.
- Foreign function calls (for example, to external libraries).
- Functions that can
[raise errors](/docs/manual/functions/#raising-and-non-raising-functions).
In addition, the compiler can't run functions on the GPU. Compile-time
functions in GPU code are actually run on the CPU.
When running code, the compiler can allocate memory and instantiate types
that allocate memory, such as strings and collections. With some
limitations, it can pass compile-time values on to run-time code, a process
called _materialization_. For more information, see the section on
[materialization](/docs/manual/metaprogramming/materialization/).
## `comptime` values
It is very common to want to _name_ compile-time values. Whereas `var`
defines a runtime value, we need a way to define a named compile-time
constant. For this, Mojo uses a `comptime` declaration. At its simplest,
`comptime` can be used to define a constant value:
```mojo
comptime rows = 512
```
A `comptime` value is always evaluated at compile time, so you can use
`comptime` to force a function to run at compile time. You can use this to
calculate constant values based on information available at compile time,
such as hardware parameters.
```mojo
comptime block_size = _calculate_block_size()
```
Types are another common use for `comptime` values. Because types are
compile-time expressions, you can use a `comptime` value as a shorthand (a
type alias or "typedef") for a parameterized type:
```mojo
comptime Float16 = SIMD[DType.float16, 1]
comptime UInt8 = SIMD[DType.uint8, 1]
var x: Float16 = 0 # Float16 works like a "typedef"
```
(These aliases and others are actually defined in the
[`simd` module](/docs/std/simd/#comptime-values).)
You can also parameterize a `comptime` value to express more complicated
relationships. For details, see
[Parameterized `comptime`
values](/docs/manual/parameters/#parameterized-comptime-values).
### Compile-time scope
Like `var` variables, `comptime` values obey scope, and you can use local
`comptime` values within functions as you'd expect. Unlike `var` variables,
`comptime` values can be defined at the module level, outside of any function.
The following constructs create a new compile-time scope:
- Functions. The body of a function creates a new compile-time scope.
- Compile-time flow control. Each branch of a compile-time conditional creates
its own scope. The body of a `comptime for` loop also creates its own scope.
You can only assign a `comptime` value to a given identifier once in a given
scope.
```mojo
comptime VALUE = 10
def scope_me():
print(VALUE) # prints 10
comptime VALUE = 20
# comptime VALUE = 30 # error: invalid redeclaration of VALUE
comptime if True:
comptime VALUE = 40
print(VALUE) # prints 40
print(VALUE) # prints 20
```
## Compile-time flow control
One of the simplest things you can do with metaprogramming is using compile-time
flow control to conditionalize or repeat code. Some sample uses include:
- Conditionalizing platform-specific code (CPU vs. GPU, Linux vs. macOS) without
runtime overhead.
- Unrolling loops to eliminate runtime branches.
- Handling different data types in parameterized code.
Unlike run-time flow control constructs, compile-time flow control constructs
are evaluated once, at compile time, and determine what code is actually
compiled.
### Compile-time conditionals {#comptime-if}
You can add the `comptime` keyword to any `if` condition that's based on a valid
compile-time expression (an expression that can be evaluated at compile time).
This ensures that only the live branch of the `if` statement is compiled into
the program, which can reduce your final binary size. For example:
```mojo
from std.sys import has_accelerator
def main():
comptime if has_accelerator():
run_on_gpu()
else:
run_on_cpu()
```
In this example, if no accelerator is available, the `run_on_gpu()` function is
never called, or even compiled.
The `comptime if` statement can include `elif` and `else` branches just like a
standard `if` statement.
### Compile-time loop unrolling {#comptime-for}
You can add the `comptime` keyword to a `for` loop to create a loop that's
fully unrolled at compile time. You should generally use this only for loops
with small loop bodies and low iteration counts.
The loop sequence must be a valid compile-time expression (that is, an
expression that can be evaluated at compile time). For example, if you use
`for i in range(LIMIT)`, the expression `range(LIMIT)` defines the loop
sequence. This is a valid compile-time expression if `LIMIT` is a parameter,
`comptime` value, or integer literal.
The compiler fully unrolls the loop by replacing the `for` loop with
`LIMIT` copies of the loop body. The induction variable is replaced with a
compile-time constant value for each "iteration." For example:
```mojo
comptime for i in range(1, 5):
b[i-1] = a[i] + a[i-1]
```
This is effectively unrolled to the following run-time code:
```mojo
b[0] = a[1] + a[0]
b[1] = a[2] + a[1]
b[2] = a[3] + a[2]
b[3] = a[4] + a[3]
```
This unrolled loop compiles to branchless machine code, unlike a normal `for`
loop, which includes a bounds test at every iteration. This can be especially
important on GPU, to avoid
[thread divergence](https://max.modular.com/gpu/block-and-warp/#warp-level-synchronization).
The `comptime for` construct unrolls at the beginning of compilation, which can
greatly expand both the code size and the compilation time.
## How the compiler runs code
The process of evaluating compile-time code involves three components of the
compiler:
- Parser. Parses the code into an intermediate representation (IR) and performs
type checking.
- Interpreter. Runs code at compile time.
- Elaborator. Substitutes concrete values for compile-time parameters and
produces concrete versions of parameterized functions and structs.
When the parser turns code into IR, it also replaces some very simple `comptime`
expressions with their values, a process called _constant folding_. For example,
the compiler can constant fold the expression `2 + 3` to `5`. Standard library
functions that are marked `@always_inline("builtin")` are constant foldable.
Compile-time expressions that can't be constant folded persist and are evaluated
in the elaborator.
When the elaborator encounters a function call in a compile-time context, it
invokes the interpreter to run the function. The interpreter then checks whether
the function being called has already been _elaborated_ to produce a concrete,
executable function. If not, the interpreter adds that function to the
elaborator's work queue, and waits until it's done. Finally, the interpreter
runs the concrete function—almost like it was a small separate program—and
passes the return value back to the elaborator, which integrates it into the
parsed IR.
When reading code, it's important to remember that when a function is being
interpreted at compile time, the function has been concretized: compile-time
conditionals have been processed, and compile-time constraints and assertions
have been tested. This sometimes _appears_ to contradict the expectation that
your code runs in the order it appears in the function. For example, if your
function includes a compile-time assertion that fails, compilation fails before
the interpreter enters the function, so no part of the function is
evaluated—even code that occurs _before_ the assertion.
---
## Comptime constraints and assertions
Mojo's constraint system lets you express program guarantees that go beyond
what the type system provides. This page explains how to use this system
effectively.
A constraint, defined with the `where` keyword, represents a precondition for
calling a function or instantiating a struct:
```mojo
def pow2[n: Int]() -> Int where n >= 0:
...
```
Here the `pow2()` function requires its `n` parameter to be greater than or
equal to 0. The expression `n >= 0` is called a *proposition*. With the
exception of very simple expressions, Mojo doesn't evaluate these propositions
literally. Instead, it analyzes them symbolically, tracking a list of
propositions that are known to be true in the current scope. Understanding this
system of symbolic propositions is key to using constraints effectively.
Mojo also supports *compile-time assertions*, which test a proposition at
compile time—if the assertion evaluates to false, compilation fails:
```mojo
comptime assert x >= 0, "x must be greater than or equal to 0."
```
## Defining constraints
You can use constraints in the following contexts:
- At the end of a function, method, or in the parameters of struct
declaration, to constrain the values that you can bind to one or more
parameters.
`def pow2[n: Int]() -> Int where n >= 0:`
A single `where` clause can constrain multiple parameters:
`def subspan[start: Int, end: Int](self) -> Self where end > start:`
Methods can gate their availability on parameters of the parent struct:
`def sort() where conforms_to(Self.T, Comparable):`
- In the trait conformance list for a struct, to declare that a struct conforms
to a trait only when certain conditions are met.
`struct MyContainer[T: AnyType](Copyable where conforms_to(T, Copyable)):`
You'll use this form, called *conditional trait conformance*, when defining
parameterized types. For details, see the section on
[conditional trait conformances](/docs/manual/generics/#conditional-trait-conformance).
## Symbolic propositions
The constraint system works with *symbolic* propositions.
```mojo
def first[size: Int](array: Array[_, size]) -> array.T where size > 0:
...
```
In this case, `size > 0` is a proposition that the constraint system tracks.
By analogy, when you annotate a type on an argument you ask the compiler to
ensure callers only pass that type. When you annotate a constraint on a
function, you ask the compiler to ensure that the proposition is true at the
call site.
But the compiler can't test every proposition at every call site without running
or interpreting unbounded amounts of code, which would vastly expand compile
times. Instead, callers need to explicitly introduce "knowledge" into the
constraint system.
### Introducing knowledge
The compiler tracks knowledge by scopes: each scope contains a set of known
true propositions, also known as "knowledge," and nested scopes accumulate
knowledge from outer scopes.
There are four ways to introduce knowledge to the system:
- Inside a struct declaration, all constraints declared on the struct are
known, because concrete instances of the struct type can only be
created when the proposition is true:
```mojo
struct List[size: Int] where size >= 0:
# Knowledge base:
# - `size >= 0`
```
- Inside a function, all constraints declared on the function are known,
because callers can only call the function if they guarantee the constraint
holds:
```mojo
def create_list[size: Int]() -> List[size] where size >= 0:
# Knowledge base:
# - `size >= 0`
```
- Inside a `comptime if`, the `if` condition is known, because the code within
the body is only instantiated if the condition is true:
```mojo
comptime if size >= 0:
# Knowledge base:
# - `size >= 0`
comptime if size.is_even():
# Knowledge base:
# - `size >= 0`
# - `size.is_even()`
```
- After a `comptime assert`, the asserted condition is known, because the
compiler won't instantiate a function if a `comptime assert` condition is
false. So any code after it is only instantiated if the assertion didn't
fire:
```mojo
comptime assert size >= 0
# Knowledge base:
# - `size >= 0`
comptime assert size.is_even()
# Knowledge base:
# - `size >= 0`
# - `size.is_even()`
```
### Satisfying constraints with knowledge
Any time you call a function that has constraints, the system inspects the set
of known-true propositions at the call site and determines whether *the known
set of propositions* is a superset of *the required set of propositions*
declared on the callee.
The constraint system treats all propositions symbolically: it doesn't know or
care what the expression means and doesn't interpret the code. With a few very
simple exceptions (discussed later), the system doesn't perform symbolic math on
your behalf. Instead, it treats these propositions as opaque and only uses
knowledge you've explicitly introduced in the calling scope.
```mojo
# A function that wants a non-empty list.
def print_first[size: Int](l: Array[Int, size]) where size >= 1:
...
# A wrapper that wants a size >= 2 list.
def print_first_two[size: Int](l: Array[Int, size]) where size >= 2:
# Error: invalid call to 'print_first': lacking evidence to prove
# correctness
print_first[size](l)
# ...
```
Mojo checks constraints without knowing all the call sites, and these
expressions can reference symbolic parameter values-for example `is_prime(x)`
where `x` is unknown. Because of this, Mojo can't interpret `is_prime()` for all
possible values of `x`, nor can it make logical deductions. For example if
`is_prime(x)` and `x > 2` are true, it can't deduce that `is_odd(x)` must also
be true.
## Compile-time assertions
Use `comptime assert` to introduce a known-true proposition at a specific
point in the code:
```mojo
comptime assert x > 0, "x must be greater than 0."
```
The message is optional. If the condition evaluates to false at compile time,
compilation fails and the compiler shows the message (or a default message if
none is specified).
Mojo adds the asserted condition to the list of "known true" propositions for
any code following the assertion.
Constraints and assertions serve complementary roles: a `where` clause exposes
a *requirement* that callers must prove, and `comptime assert` is one way to
satisfy that requirement.
## Limited evaluation of propositions
In general, it's safe to assume that the constraint system doesn't evaluate
propositions directly, and that all knowledge needs to be provided explicitly.
However, the constraint system does apply a *very
limited* amount of "smartness" for very common cases. These are provided as a
convenience and should not be treated as the norm.
The goal is to provide a consistent, predictable experience so users can
recognize these patterns as they become more familiar with the system.
### Simple implication
A known proposition of the form `A and B` can satisfy a requirement of `A` by
itself (or `B` by itself), even though symbolically they aren't identical
propositions.
```mojo
def create_list[T: Copyable, size: Int]() -> List[T] where size >= 0:
return List[T](capacity = size)
def create_even_list[T: Copyable, size: Int]() -> List[T] where (
size >= 0 and (size / 2) * 2 == size
):
# No need to individually prove `size >= 0` — it's part of the
# `and` above, so the call to `create_list` type-checks.
return create_list[T, size=size]()
def main():
var l1 = create_even_list[Int, 4]()
print(len(l1) % 2)
# Prints 0, since the list has an even number of elements.
# var l2 = create_even_list[Int, 5]() # Won't compile: 5 isn't even.
```
Similarly, `A` implies `A or B` for any `B`.
### Canonicalization
Sometimes there is more than one way to write the same expression. The system
always simplifies expressions into a normal form internally, so expressions
that aren't identical on the surface may be seen as identical by the system.
The following are self-explanatory (assume `x: Int`):
- `x > 0` == `x >= 1`
- `x >= 2` == `not (x < 2)`
- `x + x` == `2 * x`
There's a special case for function calls. When invoking functions as part of
a proposition, the system treats the entire function call as opaque. Only two
identical function calls are the same.
```mojo
def is_even(x: Int) -> Bool:
return x % 2 == 0
def needs_even[x: Int]() where is_even(x):
pass
def forward_even_bad[x: Int]() where x % 2 == 0:
# ERROR: Needs evidence for `is_even(x)`.
needs_even[x]()
def forward_even_good[x: Int]() where is_even(x):
# SUCCESS
needs_even[x]()
```
:::note Builtin functions
Some functions in the standard library can be evaluated in `where` clauses.
These functions implement simple operations on core types (for example, `Int`
numerics), which are known to the compiler. These functions and can be inlined
into the calling expression when called in a parameter context.
However, there's no way to identify these builtin functions without looking at
the source code, and whether a given function is builtin may change without
notice.
If you need a predicate that works transparently rather than opaquely, consider
implementing it as a parametric comptime value instead, which is always
inlined.
```mojo
comptime is_even[x: Int]: Bool = x % 2 == 0
```
:::
### Context-free folding
This is more of an extreme case of canonicalization than a separate category,
but may catch people by surprise.
Certain primitive operations on constants can be canonicalized into a new
constant. For example:
- `1 + 1` == `2`
- `4 % 2` == `0`
- `1 == 1` == `True`
This extends to expressions with a mix of constants and non-constants:
- `1 + x + 1` == `2 + x`
Concretely, this means you may omit explicitly providing knowledge for
propositions that are simple operations on constants:
```mojo
def create_my_list() -> List[2]:
# No need to provide evidence for `2 >= 0`.
return create_list[2]()
```
## Best practices
The examples here focus on functions (declaring constraints when writing
functions and satisfying constraints when calling functions), but the tips
generalize to other forms of constraints such as struct parameter constraints
and conditional trait conformances.
### Writing functions
When writing a function, how do you decide whether a `where` clause is right
for you?


Figure 1. Deciding when to use constraints
#### Q1: Does your function handle the entire domain of its input types?
If you can write your function so that it returns a result or throws an error
on all inputs, you don't need to care about constraints at all. Just write
your function as usual.
For example, the following functions do *not* need constraints since they're
guaranteed to return or throw:
```mojo
def create_list_opt[size: Int]() -> Optional[List[size]]:
comptime if size < 0:
return None
return ...
def create_list_raise[size: Int]() raises -> List[size]:
comptime if size < 0:
raise Error("negative size!")
return ...
```
Constraints are only for cases where you don't want to check for exceptional
cases at execution time.
- Constraints ask the type checker to rule out exceptional cases so that a
type-checked program guarantees you (as the function author) don't need to
handle these cases in your function body using run-time resources.
- As always, enforcing static guarantees isn't free. You're trading off
run-time error checking logic for compile-time proof writing. In the
absence of hard limits that prevent you from error checking at run
time, it comes down to your preference for user experience.
#### Q2: Is this limitation a central concept of your code?
If the condition represents a concept that is central to your code, a
dedicated type may be easier than sprinkling `where` everywhere.
Examples:
- A SIMD library that needs to represent SIMD width values, which aren't
arbitrary integers.
- A filesystem library that needs to represent paths that follow specific
format rules.
- A network library that needs to represent port numbers, which must be in
the valid range.
This is a good approach because the constraint is proven once (at construction)
and the refined value can be passed around unconstrained until it needs to be
disassembled. Your APIs stay simpler: fewer `where` clauses, fewer repeated
asserts.
A new type doesn't come for free though, as it's no longer freely
interoperable with the original type. Make sure that the semantics are distinct
enough to warrant the extra code. For example, writing a new type usually means
implementing dunder methods corresponding to common operators (addition,
subtraction, equality, and so on), which preserve the semantics of the new
type.
#### Q3: Is the constraint understandable and provable by the caller?
If yes, use constraints.
Make the condition part of the function's contract so that callers must prove
it.
This tends to be the right choice when:
- The condition is a user-facing requirement that the user can understand.
- Callers typically already need to handle the "bad" case themselves (for
example, they may already have a `comptime if` that branches on this
condition).
Example:
```mojo
def take_prefix[n: Int, len: Int](...) -> ... where 0 <= n <= len:
...
```
One practical heuristic: if a user can read the function signature and
immediately understand *what they did wrong* when the constraint fails,
`where` is likely the right tool.
If the constraint is *not* understandable or *not* provable by the
user, use `assert` or `abort`.
If the "bad case" isn't something users would understand, adding a `where`
constraint only causes more confusion, as users can't reasonably prove it
themselves.
- This typically happens when your library returns a value that has some
internal constraints on it and expects users to pass it back with those
constraints. For example, a communication library passes a device handle to
the user as an `Int`, which has properties that are only known internally (for
example, non-negative, special bits). Library APIs accepting this handle can't
expect the user to prove these.
- There are usually more type-safe ways to achieve the same thing, so only do
this if you don't care about type safety. For example, a more type-safe
approach is to introduce a new type for the device handle so users are less
likely to accidentally pass another `Int` or modify the returned value
unexpectedly.
#### Summary
- Use a dedicated type when the condition is a common refinement that should
be proven once and reused everywhere.
- Use `where` when the condition is a user-understandable precondition.
- Use `comptime assert` or `abort` for internal inconsistencies in your library
where a user can't do anything with the failure.
### Calling functions
When calling a function, how do you show proof that you've satisfied the
constraint?
This is where the constraint system actively guides you into handling
exceptional cases.


Figure 2. Using constrained APIs
#### Q1: Do the constrained parameters come from a parent parameter list?
If the constrained parameter you're passing is from the function's parameter
list, or is a parameter on the enclosing struct, you're basically "forwarding"
a value from one parameter list to another. Go to Q2.
If not, you computed the value inside the function body, and you're in
"construction" territory. Go to Q3.
#### Q2: Does the same limitation apply to your function?
If you're forwarding a constrained parameter into another constrained API,
it's usually a hint to **propagate** the same requirement onto your own
function.
For example, to propagate the constraint to your callers:
```mojo
def create_list[T: Copyable, size: Int]() -> List[T] where size >= 0:
return List[T](capacity = size)
# This function is also only meaningful when size >= 0.
# Make that part of the contract too.
def create_list_and_process[T: Copyable, size: Int](value: T) where size >= 0:
comptime xs = create_list[T, size=size]()
...
```
But if you want your function to accept a *wider* input domain, your job is
to **handle both cases** explicitly using a `comptime if`.
For example, narrow the domain by checking before the call:
```mojo
def create_list_and_fill[size: Int]() -> Optional[List[size]]:
comptime if size >= 0:
return needs_nonneg[size]()
else:
return None
```
#### Q3: Do you know that the parameter already satisfies this condition?
If the parameter didn't come from a parent parameter list, it must
have been computed in the body.
Decide whether the desired constraint **holds by construction**.
If it does, indicate this to the constraint system via a `comptime assert`.
Note that this is you explicitly taking over the burden of proof.
Don't be afraid to write these asserts. The symbolic nature of the constraint
system means that it is conservative—logical deductions that are obvious to
you aren't always "obvious" to it. Adding `comptime assert` is not a
code smell, but rather an inseparable part of working within the constraint
system.
For example, given a computed parameter that is always valid:
```mojo
# The constraint on hi & lo guarantees a valid size.
# Introduce this piece of knowledge explicitly.
comptime size = hi - lo
comptime assert size >= 0, "span is guaranteed non-negative"
return create_list[size]()
```
But if the constraint does *not* necessarily hold, insert a `comptime if` and
handle both cases explicitly.
For example, a computed parameter that may be invalid:
```mojo
# This version does NOT have a constraint on its inputs.
# Branch on the computation to handle both cases.
def create_list_from_span[lo: Int, hi: Int]() -> Optional[List[hi - lo]]:
comptime size = hi - lo
comptime if size >= 0:
return create_list[size]()
else:
return None
```
## Summary
- Constraints are part of the API contract.
- A `where` clause is a precondition that the **API author** requires the
**caller** to prove.
- The compiler doesn't automatically derive evidence for callers.
- The caller must explicitly provide evidence that the precondition is always
satisfied.
- If a caller gets a "lacking evidence" error, they can:
1. Add a constraint to push the requirement onto their own callers.
2. Branch on the condition (`comptime if`).
3. Assert an invariant (`comptime assert`).
---
## Intro to metaprogramming
Many languages have facilities for *metaprogramming*: writing code
that generates or modifies code. Python has facilities for dynamic
metaprogramming: features like decorators, metaclasses, and many more. These
features make Python very flexible and productive, but since they're dynamic,
they come with run-time overhead. Other languages have static or compile-time
metaprogramming features, like C preprocessor macros and C++ templates. These
can be limiting and hard to use.
Mojo's compile-time metaprogramming system uses the same language as run-time
programs, so you don't have to learn a new language—just a few new features.
The primary features you'll need to learn are:
- Compile-time statements and expressions
- Parameters
- Traits
## Compile-time statements and expressions
The `comptime` keyword identifies a statement or expression that needs to be
evaluated at compile time. For example, the `comptime` keyword is used to
declare compile-time constant values and to introduce compile-time conditionals
and loops. For information on compile-time assignments and control flow, see
[Compile-time evaluation](/docs/manual/metaprogramming/comptime-evaluation/).
## Parameters {#parameters-and-generics}
Functions and structs can be *parameterized* with compile-time parameters,
allowing you to define a container that holds different data types, or a matrix
multiplication algorithm that's parameterized by the matrix dimensions.
Compile-time parameters are similar to C++ template parameters or Rust generic
parameters. At compile time, Mojo *specializes* parameterized code to make
*concrete* versions—that is, it replaces parameters with constant values.
For example, a matrix multiplication function parameterized on its matrix
dimensions can be specialized at compile time to select the most efficient
algorithm based on those dimensions. For information on parameterization, see
[Parameters](/docs/manual/parameters/).
## Traits {#traits-and-generics}
Type-parameterized functions and structs work across many types. For example,
a list might hold `Int`, `Float32`, or `String` values. Type-parameterized
code needs to know what operations those types support.
A *trait* defines a set of behaviors that types provide. Instead of
pre-selecting specific types, a parameterized sort function can just require
`Comparable`. For more information, see [traits](/docs/manual/traits/) and
[parameterized declarations](/docs/manual/generics/).
---
## Materializing compile-time values at run time
Mojo's compile-time metaprogramming makes it easy to make calculations at
compile time for later use. The process of making a *compile-time value*
available at run time is called *materialization*. For types that can be
trivially copied, this isn't an issue. The compiler can simply insert the value
into the compiled program wherever it's needed.
```mojo
comptime threshold: Int = some_calculation() # calculate at compile time
for i in range(1000):
my_function(i, threshold) # use value at runtime
```
However, Mojo also allows you to create instances of much more complex types at
compile-time: types that dynamically allocate memory, like `List` and `Dict`.
Re-using these values at run time presents some questions, like where the memory
is allocated, who owns the values, and when the values are destroyed.
This page describes when Mojo materializes values, and presents some techniques
for avoiding unnecessary materialization of complex values.
## Implicit and explicit materialization
When you use a `comptime` value at run time, you're explicitly or implicitly
copying the value into a run-time variable:
```mojo
comptime comptime_value = 1000
var runtime_value = comptime_value
```
This process of moving a compile-time value to a run-time variable is called
*materialization*. If the value is implicitly copyable, like an `Int` or `Bool`,
Mojo treats it as *implicitly materializable* as well.
But types that **aren't** implicitly copyable present other challenges. Consider
the following code:
```mojo
def lookup_fn(count: Int):
comptime list_of_values: List[Int] = [1, 3, 5, 7]
for i in range(count):
# Some computation, doesn't matter what it is.
var idx = dynamic_function(i)
# Look up another value
var lookup = list_of_values[idx]
# Use the value
process(lookup)
```
This looks reasonable, but compiling it produces an error on this line:
```mojo
var lookup = list_of_values[idx]
```
```output
cannot materialize comptime value of type 'List[Int]' to runtime
because it is not 'ImplicitlyCopyable'
```
Just like Mojo forces you to explicitly copy a value that's expensive to
copy, it forces you to explicitly materialize values that are expensive to
materialize, by calling the
[`materialize()`](/docs/std/builtin/value/materialize/) function.
Here's the code above with explicit materialization added:
```mojo
def lookup_fn(count: Int):
comptime list_of_values: List[Int] = [1, 3, 5, 7]
for i in range(count):
var idx = dynamic_function(i)
# This is the problem
var tmp: List[Int] = materialize[list_of_values]()
var lookup = tmp[idx]
# tmp is destroyed here
process(lookup)
```
This code materializes the list of values *inside* of the loop, which includes
dynamically allocating heap memory and storing the four elements into that
memory. Because the last use of `tmp` is on the next line, the memory then gets
deallocated before the loop iterates. This creates and destroys the list on
every iteration of the loop, which is clearly wasteful.
A more efficient version would materialize the list *outside* of the loop:
```mojo
def lookup_fn(count: Int):
comptime list_of_values: List[Int] = [1, 3, 5, 7]
var list = materialize[list_of_values]()
for i in range(count):
var idx = dynamic_function(i)
var lookup = list[idx]
process(lookup)
# materialized list is destroyed here
```
This is why Mojo requires you to explicitly materialize non-trivial values; it
puts you in control of when your program allocates resources.
## Global lookup tables
Mojo doesn't currently have a general-purpose mechanism for creating global
static data. This is a problem for some performance-sensitive code where you
want to use a static lookup table. Even if you declare the table as a `comptime`
value, you need to materialize it each time you want to use the data.
The [`global_constant()`](/docs/std/builtin/globals/global_constant/) function
provides a solution for storing a compile-time value into static global storage,
so you can access it without repeatedly materializing the value. However, this
currently only works for self-contained values which don't include pointers to
other locations in memory. That rules out using collection types like `List` and
`Dict`.
The easiest way to use `global_constant()` is with
[`Array`](/docs/std/collections/array/Array/), which
allocates a statically sized array of elements on the stack. The following code
uses `global_constant()` to create a static lookup table.
```mojo
from std.builtin.globals import global_constant
def use_lookup(idx: Int) -> Int64:
comptime numbers: Array[Int64, 10] = [
1, 3, 14, 34, 63, 101, 148, 204, 269, 343
]
ref lookup_table = global_constant[numbers]()
if idx >= len(lookup_table):
return 0
return lookup_table[idx]
def main():
print(use_lookup(3))
```
At compile time, Mojo allocates the `numbers` array, and then the
`global_constant()` function copies it into static constant memory, where the
code can reference it without requiring any dynamic logic to create or populate
the array. At run time, the `lookup_table` identifier receives an immutable
reference to this memory.
Note the use of `ref lookup_table` to bind the reference returned by
`global_constant()`. Using `var lookup_table` would cause a compiler error,
because it would trigger a copy, and `Array` doesn't support implicit
copying.
## Using the `comptime` keyword
Another approach that you can use to avoid materializing a complex value is to
use the `comptime` keyword to control when Mojo evaluates an expression.
Assigning an expression to a `comptime` value causes Mojo to evaluate the
expression at compile time.
For example, if you want to force a function to run at compile time:
```mojo
comptime tmp = calculate_something() # executed at compile time
var y = x * tmp # executed at run time
```
If you're only creating a `comptime` value for a single use, you can use a
`comptime` sub-expression instead:
```mojo
var y = x * comptime (calculate_something())
```
This works exactly like the previous example, without creating a named temporary
value. The `comptime` keyword here tells Mojo to evaluate the expression inside
the parentheses (`calculate_something()`) at compile time.
For example, you can use a `comptime` sub-expression when working with the
`Layout` type, which determines how you store and retrieve data in a
`LayoutTensor`. Materializing a `Layout` requires dynamic allocation, which
isn't supported on GPUs. So calling this code on a GPU produces an error:
```mojo
comptime layout = Layout.row_major(16, 8)
var x = layout.size() // WARP_SIZE # Can't implicitly materialize layout
```
A `comptime` sub-expression fixes this issue:
```mojo
comptime layout = Layout.row_major(16, 8)
var x = comptime (layout.size()) // WARP_SIZE
```
Now, the expression `layout.size()` gets evaluated at compile time, so there's
no need to materialize the layout.
You could also achieve the same effect using a named `comptime` value.
```mojo
comptime layout = Layout.row_major(16, 8)
comptime layout_size = layout.size()
var x = layout_size // WARP_SIZE
```
The `comptime` sub-expression is just a more compact way to express the same
thing.
## Materializing literals
Literal values, like string literals and numeric literals are also materialized
to their run-time equivalents, but this is mostly handled automatically by the
compiler:
```mojo
comptime str_literal = "Hello" # at compile time, a StringLiteral
var str = str_literal # at run time, a String.
var static_str: StaticString = str_literal # or a StaticString
```
Both `String` and `StaticString` can be implicitly created from a
`StringLiteral`, but without a type annotation, Mojo defaults to materializing
`StringLiteral` as a `String`.
---
## Reflection
Reflection helps you write code that inspects its own structure at
compile time and reports information about types. This makes it
possible to build features like structural validation, automatic
comparisons, serialization, safer assertions, and richer error
messages without hardcoding details for specific type
implementations.
:::caution
Mojo reflection is newly introduced and currently incomplete. Some reflection
capabilities are limited, unstable, or not yet fully exposed through the
language interface. This page describes the direction of the feature as well
as the parts that are available today.
All examples reflect the state of the language at the time this page was
published. They may change as reflection support matures.
:::
## Why reflection?
Reflection is one of Mojo's powerful compile-time features. It lets
you inspect types, access fields, and generate code that adapts to a
struct's shape.
For example, define a struct, conform it to `Equatable`, and the `==`
operator automatically works:
```mojo
@fieldwise_init
struct Sensor(Equatable, Hashable, Writable):
var id: Int
var label: String
var reading: Float64
```
This code uses no operator overload or boilerplate.
Mojo inspects the struct at compile time, checks that each field supports
equality, and generates the comparison code. That is what reflection does.
Reflection has no runtime cost. The compiler does the work up front and
emits code as efficient as manually written code.
:::note
In this example, `Sensor` also conforms to `Hashable` and `Writable`.
By conforming, instances work as `Dict` keys or `Set` elements, and print
cleanly, without extra code.
:::
## Inspect a type
Use the `reflect[T]` alias to inspect a type at compile time. Built into
Mojo, it resolves to `Reflected[T]`, a handle type with static methods
for querying a type.
This code uses `reflect[T]` to inspect a type's structure:
```mojo
def show_type[T: AnyType]():
comptime type_name = reflect[T].name()
comptime field_count = reflect[T].field_count()
comptime field_names = reflect[T].field_names()
comptime field_types = reflect[T].field_types()
print("struct", type_name)
comptime for idx in range(field_count):
comptime field_name = field_names[idx]
comptime field_type = reflect[field_types[idx]].name()
var intro = "├──" if idx < (field_count - 1) else "└──"
print(intro, " var ", field_name, ": ", field_type, sep="")
```
Create some types to test this with:
```mojo
@fieldwise_init
struct MyStruct:
var x: String
var y: Optional[Int]
comptime DefaultItemCount = 10
struct ParameterizedStruct[
T: Movable & Deinitable, item_count: Int = DefaultItemCount
]:
var list: List[Self.T]
def __init__(out self):
self.list = List[Self.T](capacity=Self.item_count)
def main():
show_type[MyStruct](); print()
show_type[Optional[Float64]](); print()
show_type[Dict[Int, String]](); print()
show_type[ParameterizedStruct[String, item_count=5]]()
```
When run, this code prints each struct's name and fields with their types.
The `comptime for` loop over fields resolves at compile time. At runtime,
only the resulting `print()` calls execute:
```text
struct tests.MyStruct
├── var x: String
└── var y: std.collections.optional.Optional[SIMD[DType.int, 1]]
struct std.collections.optional.Optional[SIMD[DType.float64, 1]]
└── var _value: std.utils.variant.Variant[, {}]
struct std.collections.dict.Dict[SIMD[DType.int, 1], String, \
std.hashlib._ahash.AHasher[[0, 0, 0, 0] : SIMD[DType.uint64, 4]]]
├── var _table: std.collections._swisstable.SwissTable[\
SIMD[DType.int, 1], String, std.hashlib._ahash.AHasher[\
[0, 0, 0, 0] : SIMD[DType.uint64, 4]]]
└── var _order: List[SIMD[DType.int32, 1]]
struct tests.ParameterizedStruct[String, 5 : SIMD[DType.int, 1]]
└── var list: List[String]
```
The output uses compiler-resolved names, and the exact rendering may drift
as the compiler evolves.
Names appear with their parameters applied. Some print bare and others carry
a module path: `Int`, `Float64`, `Bool`, `String`, and `List` show no path,
while `Dict`, `Optional`, and `Set` show theirs. Your own structs carry the
name of the module that declares them. Aliases resolve to what they alias,
so `Int` and `Float64` show up as the one-element SIMD types underneath.
Parameter values print with their type attached.
If you only need the base type name, use `base_name()`:
```mojo
print(reflect[List[Int]].base_name()) # List
print(reflect[Dict[String, Int]].base_name()) # Dict
```
:::note
When you need one field, access it by name instead of iterating:
```mojo
comptime host_handle = reflect[Config].field["host"]
var default_host: host_handle.T = "localhost"
print(default_host) # localhost
```
Name-based lookup requires a concrete type. If `T` is parameterized, use
index-based iteration instead.
:::
## Detect field-level changes between two values
Compare two values and list which fields differ. Use this for test
assertions, audit logs, change tracking, or debugging.
```mojo
def diff_fields[T: AnyType](a: T, b: T) -> List[String]:
comptime names = reflect[T].field_names()
comptime types = reflect[T].field_types()
var diffs = List[String]()
comptime for idx in range(reflect[T].field_count()):
comptime if conforms_to(types[idx], Equatable):
ref a_val = reflect[T].field_ref[idx](a)
ref b_val = reflect[T].field_ref[idx](b)
if a_val != b_val:
diffs.append(String(comptime (names[idx])))
return diffs^
```
For example, consider a configuration type:
```mojo
@fieldwise_init
struct Config(Equatable):
var host: String
var port: Int
var verbose: Bool
var timeout: Float64
```
`diff_fields()` compares two `Config` values and returns the field names
that differ:
```mojo
def main():
var old = Config("localhost", 8080, False, 30.0)
var new = Config("localhost", 9090, True, 30.0)
var changes = diff_fields(old, new)
for name in changes:
print("changed:", name)
# changed: port
# changed: verbose
```
## Write once, reuse everywhere with traits
Reflection is powerful when used in traits with provided methods. The
method runs for any conforming struct that meets the trait requirements.
`MakeCopyable` duplicates every copyable field from one instance to another:
```mojo
trait MakeCopyable:
def copy_to(self, mut other: Self):
comptime field_count = reflect[Self].field_count()
comptime field_types = reflect[Self].field_types()
comptime Usable = Copyable & Deinitable
comptime for idx in range(field_count):
comptime field_type = field_types[idx]
comptime if conforms_to(field_type, Usable):
reflect[Self].field_ref[idx](other) = reflect[Self].field_ref[
idx
](self).copy()
```
Conforming structs receive `copy_to()` without writing an implementation.
As a trait method, `copy_to()` has direct access to `Self`. You don't
need a type parameter.
```mojo
@fieldwise_init
struct MultiType(MakeCopyable, Writable):
var w: String
var x: Int
var y: Bool
var z: Float64
def write_to[W: Writer](self, mut writer: W):
writer.write(String(t"[{self.w}, {self.x}, {self.y}, {self.z}]"))
def main():
var original = MultiType("Hello", 1, True, 2.5)
var target = MultiType("", 0, False, 0.0)
original.copy_to(target)
print(target) # [Hello, 1, True, 2.5]
```
You define the behavior once. Every conforming struct gets it as a
provided method.
## Layout, source locations, and type utilities
These tools expose lower-level details such as layout, lifetimes, and
source information.
### Field layout and byte offsets
When you need field layout for zero-copy serialization, C interop, or
alignment, use `field_offset()`:
```mojo
struct Packet:
var flags: UInt8
var id: UInt32
var payload: UInt64
def show_layout[T: AnyType]():
var names = materialize[reflect[T].field_names()]()
comptime for i in range(reflect[T].field_count()):
comptime off = reflect[T].field_offset[index=i]()
print(names[i], "at byte", off)
def main():
show_layout[Packet]()
# flags at byte 0
# id at byte 4 (alignment padding: 3 bytes)
# payload at byte 8
```
`field_offset` accepts `name=` or `index=` and accounts for alignment
padding. The gap between `flags` (1 byte) and `id` (byte 4) shows the
compiler inserting 3 bytes of padding so `id` aligns to a 4-byte boundary.
### Types and origins
Two functions provide compile-time access to type and lifetime
information from expressions:
**`type_of(x)`** returns the type of an expression for use in parameter
positions:
```mojo
def make_default[T: Defaultable]() -> T:
return T()
def main():
var x = 42
var y = make_default[type_of(x)]()
print(y) # 0
```
**`origin_of(x)`** captures the origin (lifetime and mutability) of a
reference.
In Mojo, every reference has an _origin_ that tracks which value it reads
from and whether it can mutate that value. `origin_of(x)` captures this
information at compile time so you can thread it through function
signatures.
It appears in signatures where a returned reference must be tied to an
input's lifetime:
```mojo
from std.os import abort
def first_ref[
T: Movable
](ref list: List[T]) -> ref[list[0]] T:
if not list:
abort("empty list")
return list[0]
def main():
var l: List[Int] = [1, 2, 3]
ref x = first_ref(l)
print(x) # 1
x += 10 # modifies the original list through the reference
print(l) # [11, 2, 3]
l = []
first_ref(l) # aborts with "empty list"
```
The returned reference shares its origin with `list`, so the compiler
knows it is valid as long as `list` is. Both are available without
imports.
### Source locations
**`call_location()`** returns the caller's source location, not the
location of the `call_location()` call itself. When building assertions or
validators, error messages are more useful when they point to the call
site:
```mojo
from std.reflection import call_location
@always_inline
def require(
cond: Bool, msg: String = "requirement failed"
) raises:
if not cond:
raise Error(call_location().prefix(msg))
def main() raises:
var x = 5
require(x > 10, "x must be > 10")
# Error: At /path/to/file.mojo:10:5: x must be > 10
```
Mark the enclosing function `@always_inline` (or
`@always_inline("nodebug")`). The decorator is what guarantees the location
the function reports; without it, the result depends on whether the compiler
inlined the call anyway. `call_location()` also accepts an optional
`inline_count` parameter. The default (1) reports the immediate caller's
call site. Higher values skip additional levels, and the compiler must
inline every level in the chain.
**`source_location()`** returns the location where `source_location()` is
called. This is less useful for debugging because it reports the location of
the call, not the caller:
```mojo
from std.reflection import source_location
def log(msg: String):
var loc = source_location()
print(
"[", loc.file_name(), ":", loc.line(), "] ",
msg, sep=""
)
def main():
log("starting up")
# [/path/to/file.mojo:4:15] starting up
```
### Function names
Retrieve a function's source name or linker symbol at compile time. Use
this for logging, tracing, or dispatch:
```mojo
from std.reflection import get_function_name, get_linkage_name
def process_data():
pass
def main():
print(get_function_name[process_data]()) # process_data
print(get_linkage_name[process_data]()) # mangled symbol
```
- **`get_function_name[func]()`** returns the name as written in source code.
- **`get_linkage_name[func]()`** returns the mangled symbol name.
Both take the function as a parameter value.
## Learn more
- Visit the reflection
[package documentation](/docs/std/reflection/)
for API details.
- Learn more about [traits](/docs/manual/traits/).
---
## Operators
Operators are symbols and keywords that act on values. They support
addition, comparison, bitwise operations, and boolean logic using operator
syntax instead of method calls.
Mojo's operator syntax mirrors Python. Symbols, precedence, and
associativity use Python conventions, so most behavior will feel familiar
if you've used Python, C, Rust, or similar languages.
That said, a few details are specific to Mojo. Boolean operators use words
(`and`, `or`, `not`) instead of symbols like `&&` and `||`. The ternary
expression places the condition in the middle. The caret (`^`) serves as
both bitwise XOR and the transfer sigil for ownership and memory management.
## Arithmetic
Standard arithmetic operators match what you see in most languages:
```mojo
print(7 + 3) # 10, add
print(7 - 3) # 4, subtract
print(7 * 3) # 21, multiply
```
Exponentiation uses two stars (`**`) and not a caret (`^`). If you prefer
a function form, call `pow(base, exponent)`:
```mojo
print(2 ** 8) # 256 (exponentiation)
```
### Unary symbols
Three prefix operators apply to single values. Place the operator to the
immediate left of the value without spaces:
- `-x` negates (`-7`, the operator is `-`, the expression is `7`)
- `+x` is a no-op identity (`+7`)
- `~x` inverts bits (`var a: Int8 = -128; print(~a) # 127`)
### Division and remainder
Mojo has two division operators, and the difference matters
for negative numbers:
```mojo
var a = -7
var b = 4
print(a / b) # -1 (truncates toward zero)
print(a // b) # -2 (rounds toward negative infinity)
```
- Use `/` when you want truncation toward zero.
- Use `//` when you want floor division.
For floating-point types, `/` performs standard division and `//`
returns a float rounded down to the nearest whole number.
The modulo operator `%` returns the remainder, following this rule:
```text
a == b * (a // b) + (a % b)
```
For example:
```mojo
print(7 % 3) # 1
print(-7 % 4) # 1
print(7 % -4) # -1
```
### Exponentiation
The `**` operator is right-associative, so it groups from the
right:
```mojo
print(2 ** 3 ** 2) # 512, same as 2 ** (3 ** 2)
```
Exponentiation is one of only two right-associative operators in
Mojo. The other is the ternary conditional expression (`if`-`else`).
### Matrix multiplication
The `@` operator performs matrix multiplication. If you've
used NumPy, this will look familiar.
Mojo doesn't include a built-in matrix type, but any type
that implements `__matmul__()` can use it.
## Comparisons
Mojo provides six comparison operators: `==`, `!=`, `<`,
`<=`, `>`, and `>=`. Each returns a `Bool` value:
```mojo
print(10 > 5) # True
print(10 == 10) # True
print(10 != 10) # False
```
### Floating-point comparison
Don't compare floating-point values with the equality operator (`==`).
Small rounding errors accumulate, and values that look equal often aren't:
```mojo
from std.math import isclose
var total: Float64 = 0.0
for _ in range(10):
total += 0.1
print(total == 1.0) # False
```
Use `isclose()` for approximate comparison on any
floating-point type.
```mojo
print(isclose(total, 1.0)) # True
```
### Chained comparisons
You can chain comparisons to check a range or a sequence of
conditions in one expression. Each pair is evaluated from left to
right:
```mojo
var x = 5
print(1 < x < 10) # True, 1 < x and x < 10
print(1 < x < 3) # False, 1 < x and x < 3
print(1 < x <= 5 < 9) # True, 1 < x and x <= 5 and 5 < 9
```
The expression `a < b < c` is equivalent to `(a < b) and (b < c)`.
The middle value is evaluated once, not twice. This matters when the
value comes from a function:
```mojo
var short_item_list: List[Int] = [1, 2, 3, 4, 5]
var ok = 0 < len(short_item_list) <= 10
print(ok) # True
```
Comparison, membership, and identity operators share the same precedence,
so you can combine them in a single chain. As a hypothetical example:
```mojo
5 != a < b in c is d
```
This evaluates as:
```mojo
(5 != a) and (a < b) and (b in c) and (c is d)
```
## Bitwise operations
Bitwise operators work on integer types at the bit level. They let
you inspect and manipulate individual bits directly.
AND (`&`) keeps bits that are set in both operands, OR (`|`) keeps bits set
in either, and XOR (`^`) keeps bits that differ:
```mojo
var flags: UInt8 = 0b0000_0101
var mask: UInt8 = 0b0000_0011
print(flags & mask) # 1 (only bit 0 set in both)
print(flags | mask) # 7 (bits 0, 1, and 2)
print(flags ^ mask) # 6 (bits 1 and 2 differ)
```
Left shift (`<<`) and right shift (`>>`) move bits by a given number
of positions. Left shift by *n* is equivalent to multiplying by 2^n,
right shift to dividing by 2^n:
```mojo
print(1 << 4) # 16
print(16 >> 2) # 4
print(-16 >> 2) # -4
```
Among the bitwise operators, precedence runs: NOT (`~`) is tightest,
then shift operators, then AND, then XOR, then OR. If the grouping isn't
obvious, use parentheses.
:::caution
Mojo uses the caret (`^`) for both bitwise XOR and the transfer operator.
In expressions where the meaning could be ambiguous, Mojo treats it as XOR.
For example, `x^+1` is `(x ^ (+1))`, not `((x^) + 1)`.
:::
## Boolean logic
Mojo uses words for boolean operators instead of symbols like `&&`
or `||`. The operators read like plain language:
```mojo
print(True and False) # False
print(True or False) # True
print(not True) # False
```
### Short-circuit evaluation
The `and` and `or` operators stop as soon as the result is known.
With `and`, if the left side is falsy, the right side isn't
evaluated. With `or`, if the left side is truthy, the right side is
skipped.
```mojo
def always_true() -> Bool:
print("called")
return True
# The string "called" never prints because the left side
# of `and` is already False:
print(False and always_true()) # False
```
This behavior is useful when the right side has side effects or is
expensive to compute.
### Truthiness
Types that conform to `Boolable` have a truth value, so they can be
used directly in boolean expressions and `if` conditions. The rules
are predictable: zero, empty strings, empty collections, and `None`
are falsy. Everything else is truthy.
```mojo
var name = "Mojo"
if name:
print("Name is set")
```
## Membership and identity
### `in` and `not in`
The `in` operator checks whether a collection contains a value:
```mojo
var colors: List[String] = ["red", "green", "blue"]
print("red" in colors) # True
print("yellow" not in colors) # True
```
It also works with strings to check for substrings:
```mojo
var food = "peanut butter"
if "nut" in food:
print("Contains a nut") # prints
```
### `is` and `is not`
Identity operators check whether two values refer to the same object,
not just whether they are equal. The most common use is checking
`Optional` values against `None`:
```mojo
var opt: Optional[Int] = None
if opt is None:
print("No value") # prints
opt = 42
if opt is not None:
print("Has a value") # prints
```
## String operators
Strings support concatenation with `+` and repetition with `*`:
```mojo
var greeting = "Hello" + " " + "Mojo"
print(greeting) # Hello Mojo
print("ha" * 3) # hahaha
print("=" * 40) # a line of 40 equals signs
```
:::note
When building a string from multiple values, the multi-argument
`String()` initializer is more efficient than chaining `+`:
```mojo
var result = String("Point (", x, ", ", y, ")")
# or
var result = String(t"Point({x}, {y})")
```
:::
Strings compare lexicographically. Uppercase letters sort before
lowercase:
```mojo
print("Zebra" < "ant") # True
print("bird" == "bird") # True
```
## Conditional expression
Mojo uses `if`-`else` for conditional expressions instead of `? :`.
The condition sits in the middle:
```mojo
var score = 80
var result = "pass" if score > 65 else "fail"
print(result) # pass
```
You can use this form anywhere an expression is valid, including
function arguments:
```mojo
def greet(name: String):
print("Hello,", name)
greet("Sami" if True else "Cass") # Hello, Sami
```
Like exponentiation, chained ternary expressions are right-associative.
They group from the right, which can be hard to read:
```mojo
var value = 50
var label = (
"low" if value < 10
else "high" if value > 100
else "mid"
)
print(label) # mid
```
## Assignment operators
### In-place assignment
Most binary operators have a compound assignment form: `+=`, `-=`, `*=`,
`/=`, `//=`, `%=`, `**=`, `@=`, `&=`, `|=`, `^=`, `<<=`, and `>>=`. These
update the left-hand value instead of creating a new one:
```mojo
var count = 0
count += 1
count += 1
print(count) # 2
var flags: UInt8 = 0b0000_0001
flags |= 0b0000_0100
print(flags) # 5 (bits 0 and 2 set)
```
For types that store data on the heap, in-place operators can avoid
allocating intermediate values. A type must implement its in-place methods
explicitly, so not every type that supports `+` also supports `+=`.
### Walrus operator
The walrus operator (`:=`, officially an *assignment expression*) assigns a
value inside an expression. The assigned value becomes the result of that
expression.
Press Return without entering text to terminate the loop. Non-empty strings are
truthy:
```mojo
while (var name := input("Name or return: ")):
print("Hello,", name)
```
`input()` is a raising function, so call it from a raising function or
handle its errors with `try`.
## Precedence
When an expression mixes operators, precedence determines what runs first.
From tightest to loosest, they are: calls and attribute access,
exponentiation, unary prefix operators, arithmetic (multiply and divide
before add and subtract), shifts, bitwise operators (AND before XOR before
OR), comparisons, boolean logic (`not` before `and` before `or`),
conditional expression, and the walrus operator.
When in doubt, use parentheses. They cost nothing at runtime and make
your intent clear both when you write the code and when it is later read
and maintained:
```mojo
# Clear without thinking about precedence
var ready = (age >= 18) and (score > threshold)
```
Assignment operators (`=`, `+=`, `-=`, and others) are
statements, not expressions. You can't mix them into
expressions.
---
## Modules and packages
This page describes how to organize your project into modules (files) and
packages (directories) that you can import into other Mojo code (and
[into Python code](/docs/manual/python/mojo-from-python/)).
If you want to package your project for distribution, instead see the
[Packaging guide](/docs/tools/packaging/).
## Mojo modules
To understand Mojo packages, you first need to understand Mojo modules. A
Mojo module is a single Mojo source file that includes code suitable for use
by other files that import it. For example, you can create a module
to define a struct such as this one:
```mojo title="mymodule.mojo"
struct MyPair:
var first: Int
var second: Int
def __init__(out self, first: Int, second: Int):
self.first = first
self.second = second
def dump(self):
print(self.first, self.second)
```
Notice that this code has no `main()` function, so you can't execute
`mymodule.mojo`. However, you can import this into another file with a
`main()` function and use it there.
For example, here's how you can import `MyPair` into a file named `main.mojo`
that's in the same directory as `mymodule.mojo`:
```mojo title="main.mojo"
from mymodule import MyPair
def main():
var mine = MyPair(2, 4)
mine.dump()
```
Alternatively, you can import the whole module and then access its members
through the module name. For example:
```mojo title="main.mojo"
import mymodule
def main():
var mine = mymodule.MyPair(2, 4)
mine.dump()
```
You can also create an alias for an imported member with `as`, like this:
```mojo title="main.mojo"
import mymodule as my
def main():
var mine = my.MyPair(2, 4)
mine.dump()
```
In this example, it only works when `mymodule.mojo` is in the same directory as
`main.mojo`. Currently, you can't import `.mojo` files as modules if they
reside in other directories. That is, unless you treat the directory as a Mojo
package, as described in the next section.
:::note
A Mojo module may include a `main()` function and may also be
executable, but that's generally not the practice and modules typically include
APIs to be imported and used in other Mojo programs.
:::
## Mojo packages
A Mojo package is just a collection of Mojo modules in a directory that
includes an `__init__.mojo` file. By organizing modules together in a
directory, you can then import all the modules together or individually.
Optionally, you can also compile the package into a precompiled `.mojoc` file
that's quicker to load when used as a dependency to another Mojo compile.
You can import a package and its modules either directly from source files or
from a compiled `.mojoc` file. It makes no real difference to Mojo
which way you import a package. When importing from source files, the directory
name works as the package name, whereas when importing from a compiled package,
the filename is the package name (which you specify with the [`mojo
precompile`](/docs/cli/precompile) command—it can differ from the directory
name). For examples, see the section below about
[naming and identifiers](#package-naming-and-identifiers).
For example, consider a project with these files:
```ini
main.mojo
mypackage/
__init__.mojo
mymodule.mojo
```
`mymodule.mojo` is the same code from examples above (with the `MyPair`
struct) and `__init__.mojo` is empty.
:::note
The `__init__.mojo` file is essential. If you don't have it, Mojo won't
recognize the directory as a package and you can't import `mymodule`.
:::
In this case, the `main.mojo` file can now import `MyPair` through the package
name like this:
```mojo title="main.mojo"
from mypackage.mymodule import MyPair
def main():
var mine = MyPair(2, 4)
mine.dump()
```
This immediately works:
```sh
mojo main.mojo
```
```output
2 4
```
However, if you don't want the `mypackage` source code in the same location
as `main.mojo`, you can compile it into a precompiled file like this:
```sh
mojo precompile mypackage -o mypack.mojoc
```
:::note
A `.mojoc` file contains non-elaborated code, so you _can_ share it across
systems. The code becomes an architecture-specific executable only after it's
imported into a Mojo program that's then compiled with `mojo build`.
The `.mojoc` format is not intended as a generic distributable format, however,
as it is tied to the exact version of the compiler that produced it. Loading a
`.mojoc` file produced by one version of the compiler into another version of
the compiler will result in a compiler error.
:::
Now, you can move the `mypackage` source somewhere else, and the project files
now look like this:
```ini
main.mojo
mypack.mojoc
```
Because we named the package `mypack`, we need to fix
the import statement:
```mojo title="main.mojo"
from mypack.mymodule import MyPair
```
And the code works the same:
```sh
mojo main.mojo
```
```output
2 4
```
:::note
If you want to rename your package, you cannot simply edit the
`.mojoc` filename, because the package name is encoded in the file.
You must instead run `mojo precompile` again to specify a new name.
:::
### The `__init__` file
As mentioned above, the `__init__.mojo` file is required to indicate that a
directory should be treated as a Mojo package, and it can be empty.
Currently, top-level code is not supported in `.mojo` files, so unlike Python,
you can't write code in `__init__.mojo` that executes upon import. You can,
however, add structs and functions, which you can then import from the package
name.
However, instead of adding APIs in the `__init__.mojo` file, you can import
module members, which has the same effect by making your APIs accessible from
the package name, instead of requiring the `.`
notation.
For example, again let's say you have these files:
```ini
main.mojo
mypackage/
__init__.mojo
mymodule.mojo
```
Let's now add the following line in `__init__.mojo`:
```mojo title="__init__.mojo"
from .mymodule import MyPair
```
That's all that's in there. Now, we can simplify the import statement in
`main.mojo` like this:
```mojo title="main.mojo"
from mypackage import MyPair
```
This feature explains why some members in the Mojo standard library can be
imported from their package name, while others required the
`.` notation. For example, the
[`functional`](/docs/std/algorithm/functional/) module resides in the
`std.algorithm` package, so you can import members of that module (such as the
`map()` function) like this:
```mojo
from std.algorithm.functional import map
```
However, the `algorithm/__init__.mojo` file also includes these lines:
```mojo title="algorithm/__init__.mojo"
from .functional import *
from .reduction import *
```
So you can actually import anything from `functional` or `reduction` simply by
naming the package. That is, you can drop the `functional` name from the import
statement, and it also works:
```mojo
from std.algorithm import map
```
:::note
Which modules in the standard library are imported to the package
scope varies, and is subject to change. Refer to the [documentation for each
module](/docs/std/) to see how you can import its members.
:::
### Package naming and identifiers
Package names are taken from directory names in the case of source packages, or
the precompiled (`.mojoc`) filename for binary ones.
Note that if the package name is not a valid identifier, an escaped identifier
may be used instead:
```mojo
import `модул`
import `package-with-hyphens and a space!` as package_without_hyphens_or_a_space
def main():
`модул`.`здрасти`()
package_without_hyphens_or_a_space.hello()
```
---
## Parameterization
Many programming languages offer systems for writing parameterized or
polymorphic code, which let you write code once, and generate efficient,
specialized code at compile time.
Mojo's compile-time parameter system lets you define reusable code. A parameter
is a compile-time input to a struct or function. Parameters appear in square
brackets after the struct or function name. Parameters can take ordinary
values, like `Int` or `String`:
```mojo
def multiplier[factor: Int](x: Int) -> Int:
return x * factor
def main():
comptime times_ten = multiplier[10]
var x10 = times_ten(3)
```
Parameters accept both types and values at compile time. When a
parameter accepts a type, the result is type-parameterized code. When it
accepts a value, the result is value-parameterized code.
```mojo no-test
struct MyList[T: AnyType]:
# ... type-parameterized
struct FixedBuffer[size: Int]:
# ... value-parameterized
```
Mojo's parameters are similar to C++ template parameters or Rust generic
parameters.
In Mojo, "parameter" always means a compile-time value, and
"argument" always means a run-time value.
In most other languages, a parameter is part of a declaration and an
argument is the value you pass at the call site. Mojo changes the
meaning of "parameter" to refer specifically to compile-time values.
Mojo makes this distinction visible in syntax: use `[]` for
parameters and `()` for arguments.
In addition to parameterizing structs and functions, you can also define
parameterized `comptime` values.
## Parameterized functions {#parameters-and-generics}
To define a *parameterized function*, add parameters in square brackets ahead
of the argument list. Each parameter is formatted just like an argument: a
parameter name, followed by a colon and a type. In the
following example, the function has a single parameter, `count` of type `Int`.
```mojo
def repeat[count: Int](msg: String):
comptime for i in range(count):
print(msg)
```
The [`comptime`](/docs/manual/metaprogramming/comptime-evaluation/#comptime-for)
keyword shown here
causes the `for` loop to be fully unrolled at compile time. The `comptime for`
requires the loop limits to be known at compile time. Since `count` is a
parameter, `range(count)` can be calculated at compile time.
Calling a parameterized function, you provide values for the parameters, just
like function arguments:
```mojo
repeat[3]("Hello")
```
```output
Hello
Hello
Hello
```
The compiler resolves the parameter values during compilation, and creates a
concrete version of the `repeat[]()` function for each unique parameter value.
After resolving the parameter values and unrolling the loop, the `repeat[3]()`
function would be roughly equivalent to this:
```mojo no-test
def repeat_3(msg: String):
print(msg)
print(msg)
print(msg)
```
:::note
This doesn't represent actual code generated by the compiler. By the
time parameters are resolved, Mojo code has already been transformed to an
intermediate representation in [MLIR](https://mlir.llvm.org/).
:::
If the compiler can't resolve all parameter values to constant values,
compilation fails.
### Overloading on parameters
Functions and methods can be overloaded on their parameter signatures. For
information on overload resolution, see
[Overloaded functions](/docs/manual/functions/#overloaded-functions).
## Parameters at a glance
Parameters to a function or struct appear in square brackets after a function
or struct name. Parameters always require type annotations.
When you're looking at a function or struct signature, you may
see some special characters such as `/` and `*` in the parameter list.
Here's an example:
```mojo
def my_sort[
# infer-only parameters
dtype: DType,
width: SIMDLength,
//,
# positional-only parameter
values: SIMD[dtype, width],
/,
# positional-or-keyword parameter
compare: def(Scalar[dtype], Scalar[dtype]) thin -> Int,
*,
# keyword-only parameter
reverse: Bool = False,
]() -> SIMD[dtype, width]:
```
Here, `compare` is a function-typed parameter. Because the comparator is a
noncapturing function value, the function type explicitly uses `thin`.
Here's a quick overview of the special characters in the parameter list:
- Double slash (`//`): parameters declared before the double slash are
[infer-only parameters](#infer-only-parameters).
- Slash (`/`): parameters declared before a slash are positional-only
parameters. Positional-only and keyword-only parameters follow the same rules
as [positional-only and keyword-only
arguments](/docs/manual/functions#positional-only-and-keyword-only-arguments).
- A parameter name prefixed with a star, like `*Types` identifies a
[variadic parameter](#variadic-parameters) (not shown in the example above).
Any parameters following the variadic parameter are keyword-only.
- Star (`*`): in a parameter list with no variadic parameter, a star by itself
indicates that the following parameters are keyword-only parameters.
- An equals sign (`=`) introduces a default value for an
[optional parameter](#optional-parameters-and-keyword-parameters).
## Parameterized declarations
Parameterization let functions work across multiple types, and let containers
store values of many types. For example,
[`List`](/docs/std/collections/list/List/) takes a type parameter, so
`List[Int]` holds integers and `List[String]` holds strings.
In Mojo, parameterizations use compile-time elements. A function parameterized
on a type is type-parameterized. A function parameterized on a value is
value-parameterized. Both use `[]`.
This function uses both a type parameter and a value parameter:
```mojo
def repeat[
MsgType: Writable, // infer-only
count: Int
](msg: MsgType):
comptime for _ in range(count):
print(msg)
def main():
repeat[2](42) # prints 42 on two lines
```
`MsgType` is type-parameterized. It accepts any `Writable` type. `count` is
value-parameterized. It accepts a compile-time integer. Together, they let
you write one function that works across types and specializes for
different repeat counts.
`MsgType` uses `//` to mark it as an
[infer-only parameter](#infer-only-parameters). The compiler infers
the type from `msg`, so you only pass `count` explicitly.
For more on parameterized declarations, including trait conformance, conditional
conformance, and value parameterizations, see
[parameterized declarations](/docs/manual/generics/).
## Parameterized structs
You can also add parameters to structs. You can use parameterized structs to
build parameterized collections. For example, a parameterized array type might
include code like this:
```mojo
from std.memory.alloc import alloc, dealloc, ThinAllocation, Layout
struct ParameterizedArray[T: Copyable & Deinitable](
Writable where conforms_to(T, Writable)
):
var _data: ThinAllocation[Self.T]
var _size: Int
def __init__(out self, var *elements: Self.T):
self._size = len(elements)
self._data = alloc[Self.T]({count = self._size}).into_thin()
var ptr = self._data.unsafe_ptr()
for i in range(self._size):
ptr.unsafe_offset(i).unsafe_write(elements[i].copy())
def __init__(out self, *, count: Int, value: Self.T):
self._size = count
self._data = alloc[Self.T]({count = count}).into_thin()
var ptr = self._data.unsafe_ptr()
for i in range(self._size):
ptr.unsafe_offset(i).unsafe_write(copy=value)
def __deinit__(deinit self):
var ptr = self._data.unsafe_ptr()
for i in range(self._size):
ptr.unsafe_offset(i).unsafe_deinit_pointee()
dealloc(self._data^.unsafe_with_layout({count = self._size}))
def __getitem__(self, i: Int) raises -> ref[self] Self.T:
if i < self._size:
return self._data.unsafe_ptr().unsafe_origin_cast[
origin_of(self)
]()[unsafe_offset=i]
else:
raise Error("Out of bounds")
def write_to(
self, mut writer: Some[Writer]
) where conforms_to(Self.T, Writable):
writer.write("[")
var ptr = self._data.unsafe_ptr()
for i in range(self._size):
writer.write(ptr[unsafe_offset=i])
if i < self._size - 1:
writer.write(", ")
writer.write("]")
```
This struct has a single parameter, `T`, which is a placeholder for
the data type you want to store in the array, sometimes called a *type
parameter*. `T` conforms to the
[`Copyable`](/docs/std/traits/copyable/Copyable/) trait and therefore to the
[`Movable`](/docs/std/traits/movable/Movable/) trait.
As with parameterized functions, you need to pass in parameter values when you
use a parameterized struct. In this case, when you create an instance of
`ParameterizedArray`, you need to specify the type you want to store, like
`Int`, or `Float64`. (This is a little confusing, because the *parameter value*
you're passing in this case is a *type*. That's OK: a Mojo type is a valid
compile-time value.)
You'll see that `Self.T` is used throughout the struct where you'd
usually see a type name. For example, as the formal type for the `elements` in
the initializer, and the return type of the `__getitem__()` method.
Here's an example of using `ParameterizedArray`:
```mojo
var array = ParameterizedArray(1, 2, 3)
print(array)
```
```output
[1, 2, 3]
```
A parameterized struct can use the `Self` type to represent a concrete instance
of the struct (that is, with all its parameters specified). For example, you
could add a static factory method to `ParameterizedArray` with the following
signature:
```mojo no-test
struct ParameterizedArray[ElementType: Copyable & Deinitable]:
...
@staticmethod
def splat(count: Int, value: Self.T) -> Self:
# Create a new array with count instances of the given value
return Self(count=count, value=value)
```
Here, `Self` is equivalent to writing `ParameterizedArray[Self.ElementType]`.
That is, you can call the `splat()` method like this:
```mojo
var float_array = ParameterizedArray[Float64].splat(8, 0)
```
The method returns an instance of `ParameterizedArray[Float64]`.
### Referencing struct parameters
As shown in the previous section, you reference a
struct parameter using dot syntax, just like a struct method or field (for
example, `Self.T`).
This struct parameter access works anywhere, not just inside a struct's methods.
You can access parameters as attributes on the type itself:
```mojo
def on_type():
print(SIMD[DType.float32, 2].length) # prints 2
```
Or as attributes on an *instance* of the type:
```mojo
def on_instance():
var x = SIMD[DType.int32, 2](4, 8)
print(x.dtype) # prints int32
```
### `comptime` members
You can also define `comptime` values as members of a `struct` or `trait`
declaration:
```mojo
struct Circle[radius: Float64]:
comptime pi = 3.14159265359
comptime circumference = 2 * Self.pi * Self.radius
```
These `comptime` members have a number of uses:
- Constant values specific to the type.
- Constant values calculated based on the struct's parameters.
- Associated types based on the struct's parameters.
The difference between parameters and `comptime` members is that parameter
values are specified by the user, but `comptime` members represent either
constant values or values derived from the input parameters.
A required value is a `comptime` member without an initializer. Conforming
types must provide a value for it. This is useful when the trait needs a
compile-time constant that varies across conforming types. For example, a
Measurable trait might require a `unit` string and an `always_positive`
boolean to validate measurements.
Common trait-provided values include constants (like seeds), and trait
compositions for refinements.
Referencing `comptime` members works just like referencing struct
parameters. You can reference a member using dot syntax (such as
`Self.IteratorType`).
#### `comptime` members as enumerations
Some Mojo types use `comptime` members to express enumerations. For example, the
following code defines a `Sentiment` type that defines `comptime` constants
for different sentiment values:
```mojo
@fieldwise_init
struct Sentiment(Equatable, ImplicitlyCopyable):
var _value: Int
comptime NEGATIVE = Sentiment(0)
comptime NEUTRAL = Sentiment(1)
comptime POSITIVE = Sentiment(2)
def __eq__(self, other: Self) -> Bool:
return self._value == other._value
def __ne__(self, other: Self) -> Bool:
return not (self == other)
def is_happy(s: Sentiment):
if s == Sentiment.POSITIVE:
print("Yes. 😀")
else:
print("No. ☹️")
```
This pattern provides a type-safe enumeration.
The [`DType`](/docs/std/builtin/dtype/DType/) struct implements a simple enum
using `comptime` members like this. This allows clients to use values like
`DType.float32` in parameter expressions or run-time expressions.
#### `comptime` members as associated types
Associated types are a common use for `comptime` members. For example, a
`List[T]` struct holds values of type `T`. The list's `__iter__()` method
returns a list iterator that returns values of type `T`. `List` uses a
`comptime` member, `IteratorType`, to define the type of the returned iterator.
The following code excerpt shows a simplified version of some of the `List`
code, showing the `List` and its associated `IteratorType`:
```mojo no-test
@fieldwise_init
struct _ListIter[
mut: Bool,
//,
T: Copyable,
origin: Origin[mut],
](ImplicitlyCopyable, Iterable, Iterator):
comptime Element = Self.T # Required by the Iterator trait
var index: Int
var src: Pointer[List[Self.Element], Self.origin]
# ... implementation omitted
struct List[T: Copyable](
Boolable, Copyable, Defaultable, Iterable, Sized
):
comptime IteratorType[
iterable_mut: Bool, //, iterable_origin: Origin[iterable_mut]
]: Iterator = _ListIter[Self.T, iterable_origin]
# ... code omitted
def __iter__(ref self) -> Self.IteratorType[origin_of(self)]:
return {0, Pointer(to=self)}
# ... code omitted
```
The `IteratorType` member is parameterized on an origin, so it can
represent both mutable and immutable iterators.
### Struct methods
A struct's method can take its own parameters. For example, the `SIMD.slice()`
method takes a `size` parameter:
```mojo
var m = SIMD[DType.int32, 4](1, 3, 5, 7)
var n = m.slice[2]()
print(n) # prints [1, 3]
```
A struct's lifecycle methods (`__init__()` and `__deinit__()`) are an
exception to this rule—they can't take parameters.
### Case study: the SIMD type
For a real-world example of a parameterized type, let's look at the
[`SIMD`](/docs/std/simd/SIMD/) type from Mojo's standard library.
[Single instruction, multiple data (SIMD)](https://en.wikipedia.org/wiki/Single_instruction,_multiple_data)
is a parallel processing technology built into many modern CPUs, GPUs, and
custom accelerators. SIMD allows you to perform a single operation on multiple
pieces of data at once. For example, if you want to take the square root of each
element in an array, you can use SIMD to parallelize the work.
Processors implement SIMD using low-level vector registers in hardware that hold
multiple instances of a scalar data type. To use the SIMD instructions
on these processors, the data must be shaped into the proper SIMD width
(data type) and length (vector size). Processors may support 512-bit or
longer SIMD vectors, and support many data types from 8-bit integers to 64-bit
floating point numbers, so it's not practical to define all of the possible SIMD
variations.
Mojo's [`SIMD`](/docs/std/simd/SIMD/) type (defined as a struct)
exposes the common SIMD operations through its methods, and takes the SIMD data
type and length values as parameters. This allows you to directly map your data
to the SIMD vectors on any hardware.
Here's a cut-down (non-functional) version of Mojo's `SIMD` type definition:
```mojo no-test
struct SIMD[dtype: DType, length: Int]:
var value: … # Some low-level MLIR stuff here
# Create a new SIMD from a number of scalars
def __init__(out self, *elems: SIMD[Self.dtype, 1]): ...
# Fill a SIMD with a duplicated scalar value.
@staticmethod
def splat(x: SIMD[Self.dtype, 1]) -> SIMD[Self.dtype, Self.length]: ...
# Cast the elements of the SIMD to a different elt type.
def cast[target: DType](self) -> SIMD[target, Self.length]: ...
# Many standard operators are supported.
def __add__(self, rhs: Self) -> Self: ...
```
So you can create and use a SIMD vector like this:
```mojo
var vector = SIMD[DType.int16, 4](1, 2, 3, 4)
vector = vector * vector
for i in range(4):
print(vector[i], end=" ")
```
```output
1 4 9 16
```
As you can see, a simple arithmetic operator like `*` applied to a pair of
`SIMD` vector operates on the corresponding elements in each vector.
Defining each SIMD variant with parameters is great for code reuse because the
`SIMD` type can express all the different vector variants statically, instead of
requiring the language to pre-define every variant.
Because `SIMD` is a parameterized type, the `self` argument in its functions
carries those parameters—the full type name is `SIMD[dtype, length]`. Although
it's valid to write this out (as shown in the return type of `splat()`), this
can be verbose, so we recommend using the `Self` type (from
[PEP673](https://peps.python.org/pep-0673/)) like the `__add__()` example does.
## Using parameterized types and functions
You can use parameterized types and functions by passing values to the
parameters in square brackets. For example, for the `SIMD` type above, `dtype`
specifies the data type and `length` specifies the number of elements in the
SIMD vector (which must be a power of 2):
```mojo
# Make a vector of 4 floats.
var small_vec = SIMD[DType.float32, 4](1.0, 2.0, 3.0, 4.0)
# Make a big vector containing 1.0 in float16 format.
var big_vec = SIMD[DType.float16, 32](1.0)
# Do some math and convert the elements to float32.
var bigger_vec = (big_vec + big_vec).cast[DType.float32]()
```
Note that the `cast()` method also needs a parameter to specify the type you
want from the cast (the method definition above expects a `target` parameter
value). Thus, just as the `SIMD` struct is a parameterized type definition, the
`cast()` method is a parameterized method definition. At compile time, the
compiler creates a concrete version of the `cast()` method with the target
parameter bound to `DType.float32`.
The code above shows the use of concrete types (that is, the parameters are all
bound to known values). But the major power of parameters comes from the ability
to define parameterized algorithms and types (code that uses the parameter
values). For example, here's how to define a parameterized algorithm with
`Scalar` that is datatype agnostic:
```mojo
from std.math import sqrt
def rsqrt[dt: DType](x: Scalar[dt]) -> Scalar[dt]:
return 1 / sqrt(x)
def main():
var v = Scalar[DType.float16](42)
print(rsqrt(v))
```
```output
0.154296875
```
When you write a type expression with square brackets, like `List[Int]`, you
must bind (specify a value for) all of the type's parameters. There are two
exceptions to this rule:
- You can explicitly unbind one or more parameters to create a
[partially-bound or unbound type](#partially-bound-and-unbound-types).
- You can omit parameters if Mojo can infer them from context.
### Parameter inference
The Mojo compiler can often *infer* parameter values, so you don't always have
to specify them. For example, in the previous section, this is how we called
the parameterized `rsqrt()` function:
```mojo
var v = Scalar[DType.float16](42)
print(rsqrt(v))
```
The compiler infers the `dt` parameter based on the type of the `v`
value passed into it, as if you wrote `rsqrt[DType.float16](v)` explicitly.
Figure 1 shows a mental model for how parameter inference works.


Figure 1. Parameter inference
Parameter inference can seem a little confusing: it might seem like the compiler
is inferring compile-time parameter values from run-time argument values. But in
fact it's inferring parameters from the statically-known *types* of the
arguments.
:::note Inference failures
If parameter inference fails, the compiler reports an error, usually "failed
to infer parameter 'param_name'". Unfortunately, the compiler also sometimes
reports this error incorrectly, for example, when the actual error is a type
mismatch. In these cases, specifying the missing parameters explicitly often
allows Mojo to report the correct error.
:::
Mojo can also infer the values of struct parameters from the arguments passed to
an initializer or static method.
For example, consider the following struct:
```mojo
struct One[Type: Writable & Copyable & Deinitable]:
var value: Self.Type
def __init__(out self, value: Self.Type):
self.value = value.copy()
def use_one():
var s1 = One(123) # equivalent to One[Int](123)
var s2 = One("Hello") # equivalent to One[String]("Hello")
```
Note that you can create an instance of `One` without specifying the `Type`
parameter—Mojo can infer it from the `value` argument.
You can also infer parameters from a parameterized type passed to an
initializer or static method:
```mojo
struct Two[Type: Writable & Copyable & Deinitable]:
var val1: Self.Type
var val2: Self.Type
def __init__(out self, one: One[Self.Type], another: One[Self.Type]):
self.val1 = one.value.copy()
self.val2 = another.value.copy()
print(String(self.val1), String(self.val2))
@staticmethod
def fire(thing1: One[Self.Type], thing2: One[Self.Type]):
print("🔥", String(thing1.value), String(thing2.value))
def use_two() raises:
var s3 = Two(One("infer"), One("me")) # prints: infer me
Two.fire(One(1), One(2)) # prints: 🔥 1 2
# Two.fire(One("mixed"), One(0))
# Error: parameter inferred to two different values
```
`Two` takes a `Type` parameter, and its initializer takes values of type
`One[Type]`. When constructing an instance of `Two`, you don't need to specify
the `Type` parameter, since it can be inferred from the arguments.
Similarly, the static `fire()` method takes values of type `One[Type]`, so Mojo
can infer the `Type` value at compile time. Note that passing two instances of
`One` with different types doesn't work.
:::note
If you're familiar with C++, you may recognize this as similar to Class Template
Argument Deduction (CTAD).
:::
## Parameter declarations
When you declare parameters on a struct or function, you have many of the same
options as you have with arguments—you can define optional parameters with
default values; keyword-only parameters; and variadic parameters.
In addition, you can define *infer-only parameters*, which provide a flexible
way of defining dependencies between parameterized types.
### Optional parameters and keyword parameters
Just as you can specify [optional
arguments](/docs/manual/functions#optional-arguments) in function signatures,
you can also define an optional *parameter* by giving it a default value.
You can also pass parameters by keyword, just like you can use
[keyword arguments](/docs/manual/functions/#keyword-arguments).
For a function or struct with multiple optional parameters, using keywords
allows you to pass only the parameters you want to specify, regardless of
their position in the function signature.
For example, here's a function with two parameters, each with a default value:
```mojo
def speak[a: Int = 3, msg: String = "woof"]():
print(msg, a)
def use_defaults():
speak() # prints 'woof 3'
speak[5]() # prints 'woof 5'
speak[7, "meow"]() # prints 'meow 7'
speak[msg="baaa"]() # prints 'baaa 3'
```
Recall that when a parameterized function is called, Mojo can
[infer the parameter values](#parameter-inference). That is, it can determine
its parameter values from the parameters attached to an argument. If the
parameterized function also has a default value defined, then the inferred
parameter value takes precedence.
For example, in the following code, we update the parameterized `speak[]()`
function to take an argument with a parameterized type. Although the function
has a default parameter value for `a`, Mojo instead uses the inferred `a`
parameter value from the `bar` argument (as written, the default `a` value can
never be used, but this is just for demonstration purposes):
```mojo
@fieldwise_init
struct Bar[v: Int]:
pass
def speak[a: Int = 3, msg: String = "woof"](bar: Bar[a]):
print(msg, a)
def use_inferred():
speak(Bar[9]()) # prints 'woof 9'
```
As mentioned above, you can also use optional parameters and keyword
parameters in a struct:
```mojo
struct KwParamStruct[greeting: String = "Hello", name: String = "🔥mojo🔥"]:
def __init__(out self):
print(Self.greeting, Self.name)
def use_kw_params():
var a = KwParamStruct[]() # prints 'Hello 🔥mojo🔥'
var b = KwParamStruct[name="World"]() # prints 'Hello World'
var c = KwParamStruct[greeting="Hola"]() # prints 'Hola 🔥mojo🔥'
```
:::note
Mojo supports positional-only and keyword-only parameters, following the same
rules as [positional-only and keyword-only
arguments](/docs/manual/functions#positional-only-and-keyword-only-arguments).
:::
### Variadic parameters
Mojo also supports variadic parameters, similar to
[Variadic arguments](/docs/manual/functions/#variadic-arguments):
```mojo
struct MyTensor[*dimensions: Int]:
pass
```
Variadic parameters currently have some limitations that variadic arguments
don't have:
- Variadic parameters must be homogeneous—that is, all the values must be the
same type.
- The parameter type must be register-passable.
Variadic keyword parameters (for example, `**kwparams`) are
not supported yet.
### Infer-only parameters
Sometimes you need to declare functions where parameters depend on other
parameters. Because the signature is processed left to right, a parameter can
only *depend* on a parameter earlier in the parameter list. For example:
```mojo no-test
def dependent_type[dtype: DType, value: Scalar[dtype]]():
print("Value: ", value)
print("Value is floating-point: ", dtype.is_floating_point())
dependent_type[DType.float64, Float64(2.2)]()
```
```output
Value: 2.2000000000000002
Value is floating-point: True
```
You can't reverse the position of the `dtype` and `value` parameters, because
`value` depends on `dtype`. However, because `dtype` is a required parameter,
you can't leave it out of the parameter list and let Mojo infer it from `value`:
```mojo no-test
dependent_type[Float64(2.2)]() # Error!
```
Infer-only parameters are a special class of parameters that are **always**
either inferred from context or specified by keyword. Infer-only parameters are
placed at the **beginning** of the parameter list, set off from other parameters
by the `//` sigil:
```mojo no-test
def example[T: Copyable, //, list: List[T]]()
```
Transforming `dtype` into an infer-only parameter solves this problem:
```mojo
def dependent_type[dtype: DType, //, value: Scalar[dtype]]():
print("Value: ", value)
print("Value is floating-point: ", dtype.is_floating_point())
```
```mojo
dependent_type[Float64(2.2)]()
```
```output
Value: 2.2000000000000002
Value is floating-point: True
```
Because infer-only parameters are declared at the beginning of the parameter
list, other parameters can depend on them, and the compiler always attempts
to infer the infer-only values from bound parameters or arguments.
There are sometimes cases where it's useful to specify an infer-only parameter
by keyword. For example, the
[`Span`](/docs/std/collections/span/Span/) type
is parameterized on [origin](/docs/manual/values/lifetimes/):
```mojo no-test
struct Span[mut: Bool, //, T: Copyable, origin: Origin[mut]]:
# ... implementation omitted
```
Here, the `mut` parameter is infer-only. The value is usually inferred when you
create an instance of `Span`. Binding the `mut` parameter by keyword lets you
define a `Span` that requires a mutable origin.
```mojo
def mutate_span(span: Span[mut=True, Byte, _]):
for i in range(0, len(span), 2):
if i + 1 < len(span):
span.swap_elements(i, i + 1)
```
If the compiler can't infer the value of an infer-only parameter, and it's not
specified by keyword, compilation fails.
## Parameter expressions are just Mojo code
A parameter expression is any code expression (such as `a+b`) that occurs where
a parameter is expected. Parameter expressions support operators and function
calls, just like run-time code, and all parameter types use the same type
system as the run-time program (such as `Int` and `DType`).
Because parameter expressions use the same grammar and types as run-time
Mojo code, you can use many
["dependent type"](https://en.wikipedia.org/wiki/Dependent_type) features. For
example, you might want to define a helper function to concatenate two SIMD
vectors:
```mojo
def concat[
dtype: DType, ls_size: Int, rh_size: Int, //
](lhs: SIMD[dtype, ls_size], rhs: SIMD[dtype, rh_size]) -> SIMD[
dtype, ls_size + rh_size
]:
var result = SIMD[dtype, ls_size + rh_size]()
comptime for i in range(ls_size):
result[i] = lhs[i]
comptime for j in range(rh_size):
result[ls_size + j] = rhs[j]
return result
```
Note that the resulting length is the sum of the input vector lengths, and a
simple `+` operation expresses this.
### Powerful compile-time programming
While simple expressions are useful, sometimes you want to write imperative
compile-time logic with control flow. You can even do compile-time recursion.
For instance, here is an example "tree reduction" algorithm that sums all
elements of a vector recursively into a scalar:
```mojo
def slice[
dtype: DType, size: Int, //
](x: SIMD[dtype, size], offset: Int) -> SIMD[dtype, size // 2]:
comptime new_size = size // 2
var result = SIMD[dtype, new_size]()
for i in range(new_size):
result[i] = Scalar[dtype](x[i + offset])
return result
def reduce_add(x: SIMD) -> Int:
comptime if x.length == 1:
return Int(x[0])
elif x.length == 2:
return Int(x[0]) + Int(x[1])
# Extract the top/bottom halves, add them, sum the elements.
comptime half_size = x.length // 2
var lhs = slice(x, 0)
var rhs = slice(x, half_size)
return reduce_add(lhs + rhs)
def main():
var x = SIMD[DType.int, 4](1, 2, 3, 4)
print(x)
print("Elements sum:", reduce_add(x))
```
```output
[1, 2, 3, 4]
Elements sum: 10
```
This makes use of the
[`comptime if`](/docs/manual/metaprogramming/comptime-evaluation/#comptime-if)
statement, which is an `if` statement that runs at compile-time. It requires
that its condition be a valid parameter expression, and ensures that only the
live branch of the `if` statement is compiled into the program. This is similar
to use of the `comptime for` loop shown earlier.
## Parameterized `comptime` values
A *parameterized `comptime` value* is a compile-time expression that takes a
list of parameters and returns a compile-time constant value:
```mojo
comptime AddOne[a: Int] : Int = a + 1
comptime nine = AddOne[8]
```
As you can see in the previous example, a parameterized `comptime` value is a
little like a *compile-time-only function*. A regular function or method can
also be invoked at compile time:
```mojo
def add_one(a: Int) -> Int:
return a + 1
comptime ten = add_one(9)
```
A major difference between a function and a parameterized `comptime` value is
that the value of a `comptime` expression can be a type, while a function can't
return a type as a value.
```mojo no-test
# Does not work—-dynamic type values not permitted
def int_type() -> AnyType:
return Int
# Works
comptime IntType = Int
```
Because a `comptime` value can be a type, you can use parameterized `comptime`
values to express new types:
```mojo
comptime TwoOfAKind[dt: DType] = SIMD[dt, 2]
var twoFloats = TwoOfAKind[DType.float32](1.0, 2.0)
comptime StringKeyDict[ValueType: Copyable & Deinitable] = Dict[
String, ValueType
]
var b: StringKeyDict[UInt8] = {"answer": 42}
```
Parameterized `comptime` declarations support the same features as parameterized
structs or functions: infer-only parameters, keyword-only and optional
parameters, [automatic parameterization](#automatic-parameterization), and so
on.
```mojo
comptime Floats[size: Int, half_width: Bool = False] = SIMD[
(DType.float16 if half_width else DType.float32), size
]
var floats = Floats[2](6.0, 8.0)
var half_floats = Floats[2, True](10.0, 12.0)
```
## Partially-bound and unbound types
A parameterized type with its parameters specified is said to be *fully-bound*.
That is, all of its parameters are bound to values. As mentioned before, you can
only instantiate a fully-bound type (sometimes called a *concrete type*).
However, parameterized types can be *unbound* or *partially bound* in some
contexts. For example, you can use `comptime` to create a type alias to a
partially-bound type to create a new type that requires fewer parameters:
```mojo
comptime StringKeyDict = Dict[String, _]
var b: StringKeyDict[UInt8] = {"answer": 42}
```
Here, `StringKeyDict` is a type alias for a `Dict` that takes `String` keys. The
underscore `_` in the parameter list indicates that the second parameter,
`V` (the value type), is unbound.
You specify the `V` parameter later, when you use `StringKeyDict`.
When used as a `comptime` value, any default values on unbound parameters are
retained until a concrete type is formed (for example, by calling a struct's
initializer):
```mojo
@fieldwise_init
struct HasDefault[x: Int, y: Int = 0]:
pass
comptime UseDefault = HasDefault[10]
```
```mojo
var instance1 = UseDefault() # instance of HasDefault[10, 0]
```
When defining parameterized APIs, you can use partially-bound and unbound types
to express type constraints with less boilerplate, a feature called
[automatic parameterization](#automatic-parameterization):
```mojo no-test
# standard declaration--explicit parameter declarations
def take_simd[dtype: DType, size: Int](value: SIMD[dtype, size]): pass
# automatically parameterized declaration
def take_floats(value: SIMD[_, _]): pass
```
You can specify a partially-bound or unbound type several ways:
- Explicitly unbind one or more parameters using an underscore (`_`) in place of
a parameter value:
```mojo
comptime StringKeyDict = Dict[String, _]
def take_floats(floats: SIMD[DType.float32, _]): pass
```
When writing a type expression like this, you must bind or explicitly unbind
every parameter, unless Mojo can infer the parameter from context. For
example, this produces an error:
```mojo
comptime Bad = Dict[String] # error: 'Dict' failed to infer parameter 'V'
```
- Explicitly unbind an arbitrary number of parameters at the end
of a parameter list using an ellipsis (`...`):
```mojo
comptime PartiallyBound = SomeComplicatedType[String, ...]
comptime Unbound = SomeComplicatedType[...]
def take_simd(v: SIMD[...]): pass
```
Using an ellipsis unbinds any remaining parameters in the list,
including keyword parameters.
- Use the bare identifier with no square brackets to specify an
unbound type:
```mojo
comptime SomeAlias = SomeComplicatedType
def take_simd2(v: SIMD): pass
```
As a matter of style, `SIMD[...]` or `SIMD[_, _]` is usually preferable to the
bare `SIMD`. The former versions are more explicit, and provide a visual cue
to readers that they're looking at a parameterized type.
### Partially-bound types versus parameterized comptime values
You may notice that the `comptime` examples in this section look similar
to the examples in the section on
[parameterized `comptime` values](#parameterized-comptime-values). For
example, you could define the `StringKeyDict` alias using either syntax:
```mojo no-test
# partially-bound type
comptime StringKeyDict = Dict[String, _]
# parameterized comptime value
comptime StringKeyDict[V] = Dict[String, V]
```
For simple type aliases, you can use either a partially-bound type or a
parameterized `comptime` value. For more complex aliases, parameterized
`comptime` values give you a great deal more flexibility.
## Automatic parameterization
Writing heavily-parameterized APIs often produces long, repetitive signatures.
For example, to define a function that takes any kind of `SIMD` value, you could
write this:
```mojo no-test
def take_simd[dtype: DType, size: Int, //](vec: SIMD[dtype, size]):
pass
```
This signature represents a function that takes any `SIMD` value,
inferring its `dtype` and `size` parameters. But it's a lot of code
to do something pretty simple.
To make it easier to write signatures like this, Mojo supports "automatic"
parameterization. Instead of explicitly naming each parameter in the argument
type, you specify a
[partially-bound or unbound type](#partially-bound-and-unbound-types):
```mojo
def take_simd(vec: SIMD[...]):
print(vec.dtype)
print(vec.length)
```
```mojo
var v = SIMD[DType.float64, 4](1.0, 2.0, 3.0, 4.0)
take_simd(v)
```
```output
float64
4
```
In the above example, the `take_simd()` function is automatically parameterized.
The `vec` argument takes a value of type `SIMD[...]`—an unbound parameterized
type. Mojo treats the unbound parameters on `vec` as infer-only parameters on
the function. This is roughly equivalent to the following code:
```mojo no-test
def take_simd[t: DType, s: Int, //](vec: SIMD[t, s]):
print(t)
print(s)
```
When you call `take_simd()` you must pass it a concrete instance of the
`SIMD` type—that is, one with all of its parameters specified, like
`SIMD[DType.float64, 4]`. The Mojo compiler *infers* the parameter
values from the input argument.
You can also use automatic parameterization with a partially bound type:
```mojo no-test
def take_floats(floats: SIMD[DType.float32, _]): pass
```
There are two important differences between a manually-parameterized
signature and an automatically-parameterized signature:
- With a manually-parameterized function, you can access the parameters by name
(for example, `t` and `s` in the previous example), which is not an option in
an automatically parameterized function.
However, you can always access a type's parameters and `comptime` members
using dot syntax—as in the automatic parameterization example, which used
`vec.dtype` and `vec.length` to access parameters on the argument.
- With the manually-parameterized function, you can pass the parameter value
directly; that's not an option with automatically-parameterized functions.
The unbound parameters are always inferred.
In addition to using automatic parameterization in the argument list of a
function, you can also use it in the parameter lists of functions, structs,
and parameterized `comptime` values.
### Examples of automatic parameterization
This section shows more examples of using automatic parameterization.
#### Automatic parameterization of parameters
You can also take advantage of automatic parameterization in the parameter list
of a function, struct, or parameterized `comptime` value. For example:
```mojo no-test
def simd_param[value: SIMD[...]]():
pass
# Equivalent to:
def simd_param[dtype: DType, size: Int, //, value: SIMD[dtype, size]]():
pass
```
Here's another example using a parameterized `comptime` value:
```mojo
comptime SomeComptime[s: SIMD[...]] = SomeStruct[s]
# Equivalent to:
comptime SomeComptime2[dtype: DType, size: Int, //, S: SIMD[dtype, size]] = SomeStruct[S]
```
#### Automatic parameterization and type expressions
As previous examples showed, you can access the parameters of an argument
or parameter value using dot syntax (`arg.param`). You can also use this
syntax inside a signature.
For example, if you want your function to take two SIMD vectors with the same
type and size, you can write code like this:
```mojo
def interleave(v1: SIMD[...], v2: type_of(v1)) -> SIMD[v1.dtype, v1.length * 2]:
var result = SIMD[v1.dtype, v1.length * 2]()
comptime for i in range(v1.length):
result[i * 2] = v1[i]
result[i * 2 + 1] = v2[i]
return result
```
```mojo
var a = SIMD[DType.int16, 4](1, 2, 3, 4)
var b = SIMD[DType.int16, 4](0, 0, 0, 0)
var c = interleave(a, b)
print(c)
```
```output
[1, 0, 2, 0, 3, 0, 4, 0]
```
As shown in the example, you can use the magic `type_of(x)` expression if you
just want to match the type of an argument. In this case, it's more convenient
and compact than writing the equivalent `SIMD[v1.dtype, v1.length]`.
#### Automatic parameterization with partially-bound types
Mojo also supports automatic parameterization: with [partially-bound
parameterized types](#partially-bound-and-unbound-types) (that is,
types with some but not all of the parameters specified).
For example, suppose you have a `Fudge` struct with three parameters:
```mojo
@fieldwise_init
struct Fudge[sugar: Int, cream: Int, chocolate: Int = 7](Writable):
pass
```
You can write a function that takes a `Fudge` argument with just one bound
parameter (it's *partially bound*):
```mojo
def eat(f: Fudge[5, ...]):
print("Ate", f)
```
The `eat()` function takes a `Fudge` struct with the first parameter (`sugar`)
bound to the value 5. The second and third parameters, `cream` and `chocolate`
are unbound.
The unbound `cream` and `chocolate` parameters become implicit parameters
on the `eat` function. In practice, this is roughly equivalent to writing:
```mojo no-test
def eat[cr: Int, ch: Int, //](f: Fudge[5, cr, ch]):
print("Ate", String(f))
```
In both cases, you can call the function by passing in an instance with the
`cream` and `chocolate` parameters bound:
```mojo
eat(Fudge[5, 5, 7]()) # Ate Fudge (5,5,7)
eat(Fudge[5, 8, 9]()) # Ate Fudge (5,8,9)
```
If you try to pass in an argument with a `sugar` value other than 5,
compilation fails, because it doesn't match the argument type:
```mojo no-test
eat(Fudge[12, 5, 7]())
# This fails because `eat()` expects `Fudge[5, 5, 7]`, but this value is
# `Fudge[12, 5, 7]`.
```
You can also explicitly unbind individual parameters. This gives you
more freedom in specifying unbound parameters.
For example, you might want to let the user specify values for `sugar` and
`chocolate`, and leave `cream` constant. To do this, replace each unbound
parameter value with a single underscore (`_`):
```mojo
def devour(f: Fudge[_, 6, _]):
print("Devoured", String(f))
```
Again, the unbound parameters (`sugar` and `chocolate`) are added as implicit
parameters on the function.
You can also unbind parameters by keyword, or mix positional and keyword
parameters, so the following function is roughly equivalent to the previous one:
the first parameter, `sugar` is explicitly unbound with the underscore
character. The `chocolate` parameter is unbound using the keyword syntax,
`chocolate=_`. And `cream` is explicitly bound to the value 6:
```mojo no-test
def devour(f: Fudge[_, chocolate=_, cream=6]):
print("Devoured", String(f))
```
Both versions of the `devour()` function work with the following calls:
```mojo
devour(Fudge[3, 6, 9]())
devour(Fudge[4, 6, 8]())
```
```output
Devoured Fudge (3,6,9)
Devoured Fudge (4,6,8)
```
## Assert parameterized type equality with `rebind()`
One of the consequences of Mojo not performing function instantiation in the
parser like C++ is that Mojo cannot always figure out whether some parameterized
types are equal and complain about an invalid conversion. This typically occurs
in static dispatch patterns. For example, the following code won't compile:
```mojo no-test
def take_simd8(x: SIMD[DType.float32, 8]):
pass
def parameterized_simd[nelts: Int](x: SIMD[DType.float32, nelts]):
comptime if nelts == 8:
take_simd8(x)
```
The parser will complain:
```plaintext
error: invalid call to 'take_simd8': argument #0 cannot be converted from
'SIMD[f32, nelts]' to 'SIMD[f32, 8]'
take_simd8(x)
~~~~~~~~~~^~~
```
This is because the parser fully type-checks the function without instantiation,
and the type of `x` is still `SIMD[f32, nelts]`, and not `SIMD[f32, 8]`, despite
the static conditional. The remedy is to manually assert the type of `x`,
using the [`rebind()`](/docs/std/builtin/rebind/rebind/) builtin, which inserts
a compile-time assertion that the input and result types resolve to the same
type after elaboration:
```mojo
def take_simd8(x: SIMD[DType.float32, 8]):
pass
def parameterized_simd[nelts: Int](x: SIMD[DType.float32, nelts]):
comptime if nelts == 8:
take_simd8(rebind[SIMD[DType.float32, 8]](x))
```
The compiler still checks that the types match, but does so later, during
elaboration. If the types don't match, compilation fails. There are fairly
simple rules for when to use `rebind()`:
- **Do** use `rebind()` when you know that two parametric types will be
identical after elaboration.
- **Don't** use `rebind()` to cast between arbitrary data types.
`rebind()` returns a reference to the rebound value. If you need to transfer the
rebound value or assign it to a variable, use the
[`rebind_var()`](/docs/std/builtin/rebind/rebind_var/) function.
---
## Intro to pointers
A pointer is an indirect reference to one or more values stored in memory. The
pointer is a value that holds an address to memory, and provides APIs to store
and retrieve values to that memory. The value pointed to by a pointer is also
known as a _pointee_.
The Mojo standard library includes several types of pointers, which provide
different sets of features. All of these pointer types are _parameterized_—they
can point to any type of value, and the value type is specified as a parameter.
For example, the following code creates an `OwnedPointer` that points to an
`Int` value:
```mojo
from std.memory import OwnedPointer
var ptr: OwnedPointer[Int]
ptr = OwnedPointer(100)
```
The `ptr` variable has a value of type `OwnedPointer[Int]`. The pointer _points
to_ a value of type `Int`, as shown in Figure 1.
![A local variable, ptr, points to an OwnedPointer[Int] which points to an Int
pointee. The value of the OwnedPointer is the address of the Int
pointee.](../images/pointers/owned-pointer-diagram.png#light)
![A local variable, ptr, points to an OwnedPointer[Int] which points to an Int
pointee. The value of the OwnedPointer is the address of the Int
pointee.](../images/pointers/owned-pointer-diagram-dark.png#dark)
Figure 1. Pointer and pointee
Accessing the memory—to retrieve or update a value—is called
_dereferencing_ the pointer. You can dereference a pointer by following the
variable name with an empty pair of square brackets:
```mojo
# Update an initialized value
ptr[] += 10
# Access an initialized value
print(ptr[])
```
## Pointer terminology
Before jumping into the pointer types, here are a few terms you'll run across.
Some of them may already be familiar to you.
- **Safe pointers**: are designed to prevent memory errors. Unless you use one
of the APIs that are specially designated as unsafe, you can use these
pointers without worrying about memory issues like double-free or
use-after-free.
- **Nullable pointers**: some languages use a sentinel value to represent
a pointer that doesn't point to anything (a "null pointer").
None of the Mojo standard library pointer types are nullable.
To model a nullable pointer, use the
[`Optional`](/docs/std/collections/optional/Optional/) type. For example,
`Optional[Pointer]` or `Optional[OwnedPointer]`.
- **Owning pointers**: own their pointees, which means that the value they point
to may be deallocated when the pointer itself is destroyed. Owning pointers
(or _smart pointers_) are responsible for allocating and deallocating memory
to hold their pointees. Non-owning pointers may point to values owned
elsewhere, or may point to dynamically-allocated memory.
- **Uninitialized memory**: refers to memory locations that haven't been
initialized with a value, which may therefore contain random data.
Newly-allocated memory is uninitialized. The safe pointer APIs don't let you
access memory that's uninitialized. The unsafe APIs can access a block of
uninitialized memory locations and then initialize them one at a time. Being
able to access uninitialized memory is unsafe by definition.
- **Copyability**: many pointer types can be copied implicitly (for example,
by assigning a value to a variable):
```mojo
var copied_ptr = ptr
```
The pointer itself is a small amount of data to copy (typically 64 bits), and
copying the pointer doesn't copy the pointee—both the original pointer and the
copy point to the same memory location and the same value.
## Pointer types
The Mojo standard library includes several pointer types with different
characteristics:
- [`Pointer`](/docs/std/memory/pointer/Pointer/) is Mojo's primary pointer type.
It points to one or more contiguous memory locations, and can refer to
uninitialized memory.
- [`OwnedPointer`](/docs/std/memory/owned_pointer/OwnedPointer/) is a smart
pointer that points to a single value, and maintains exclusive ownership of
that value.
- [`ArcPointer`](/docs/std/memory/arc_pointer/ArcPointer/) is a
reference-counted smart pointer that points to an owned value with ownership
potentially shared with other instances of `ArcPointer`.
Table 1 summarizes the different types of pointers:
| | `Pointer` | `OwnedPointer` | `ArcPointer` |
|--------------------------------------------------|----------------------------|-------------------------|-------------------------|
| Safe | Conditionally 1 | Yes | Yes |
| Memory allocation | Manual via `alloc()` | Implicit 2 | Implicit 2 |
| Owns pointee(s) | No 3 | Yes | Yes |
| Implicitly copyable | Yes | No | Yes |
| Nullable | No | No | No |
| Can point to uninitialized memory | Yes | No | No |
| Can point to multiple values (array-like access) | Yes | No | No |
Table 1. Pointer types
1 `Pointer` has both safe and unsafe methods. Unsafe methods are
named with the `unsafe_` prefix (or require an `unsafe_` keyword argument).
2 `OwnedPointer` and `ArcPointer` implicitly allocate memory when you
initialize the pointer with a value.
3 `Pointer` provides unsafe methods for initializing and destroying
instances of the stored type. The user is responsible for managing the lifecycle
of stored values.
The following sections provide more details on each pointer type.
## `Pointer`
The [`Pointer`](/docs/std/memory/pointer/Pointer/) type is Mojo's primary
pointer type. It can access a block of contiguous memory locations, which might
be uninitialized. Heap-allocated memory is accessed through a `Pointer`; the
other pointer types wrap a `Pointer` to access heap memory.
The `Pointer` type is _safe_ when used to point to an existing value:
```mojo
var ptr = Pointer(to=some_value)
print(ptr[])
```
When used this way, the `Pointer` type carries the origin of the value it points
to. It can be used to store a reference in a struct field.
The `Pointer` type also provides a number of unsafe methods you can use to
access dynamically-allocated memory, initialize and destroy stored values, and
more. These features are useful for low-level systems programming tasks, but you
need to use them with care. Some examples of _unsafe_ pointer uses include:
- Building high-performance array-like collections, such as `List`. A single
`Pointer` can access many values, and gives you a lot of control over how you
allocate, use, and deallocate memory. Being able to access uninitialized
memory means that you can preallocate a block of memory, and initialize values
incrementally as they are added to the collection.
- Interacting with external libraries including C++ and Python. You can
use `Pointer` to pass a buffer full of data to or from an external
library.
For more information, see [Using
pointers](/docs/manual/pointers/using-pointers).
## `OwnedPointer`
The [`OwnedPointer`](/docs/std/memory/owned_pointer/OwnedPointer/) type is a
smart pointer designed for cases where there is single ownership of the
underlying data. An `OwnedPointer` points to a single item, which is passed in
when you initialize the `OwnedPointer`. The `OwnedPointer` allocates memory and
moves or copies the value into the reserved memory.
```mojo no-test
from std.memory import OwnedPointer
var o_ptr = OwnedPointer(some_big_struct^)
```
An owned pointer can hold almost any type of item, but when constructing an
`OwnedPointer`, the stored item must be either `Movable` or `Copyable`.
Since an `OwnedPointer` is designed to enforce single ownership, the pointer
itself can be moved, but not copied.
`OwnedPointer` does provide an initializer that creates a new `OwnedPointer` by
copying the _stored value_ from an existing `OwnedPointer`. This results in two
owned pointers, each with its own separate allocation and its own copy of the
stored value.
## `ArcPointer`
An [`ArcPointer`](/docs/std/memory/arc_pointer/ArcPointer/) is a
reference-counted smart pointer, ideal for shared resources where the last owner
for a given value may not be clear. Like an `OwnedPointer`, it points to a
single value, and it allocates memory when you initialize the `ArcPointer` with
a value:
```mojo
from std.memory import ArcPointer
var attributesDict: Dict[String, String] = {}
var attributes = ArcPointer(attributesDict^)
```
Unlike an `OwnedPointer`, an `ArcPointer` can be freely copied. All instances
of a given `ArcPointer` share a reference count, which is incremented whenever
the `ArcPointer` is copied and decremented whenever an instance is destroyed.
When the reference count reaches zero, the stored value is destroyed and the
allocated memory is freed.
You can use `ArcPointer` to implement safe reference-semantic types. For
example, in the following code snippet `SharedDict` uses an `ArcPointer` to
store a dictionary. Copying an instance of `SharedDict` only copies the
`ArcPointer`, not the dictionary, which is shared between all of the copies.
```mojo
from std.memory import ArcPointer
struct SharedDict(ImplicitlyCopyable):
var attributes: ArcPointer[Dict[String, String]]
def __init__(out self):
var attributesDict: Dict[String, String] = {}
self.attributes = ArcPointer(attributesDict^)
def __init__(out self, *, copy: Self):
self.attributes = copy.attributes
def __setitem__(mut self, key: String, value: String):
self.attributes[][key] = value
def __getitem__(self, key: String) -> String:
return self.attributes[].get(key, default="")
def main():
var thing1 = SharedDict()
var thing2 = thing1
thing1["Flip"] = "Flop"
print(thing2["Flip"])
```
:::note
`ArcPointer` makes the reference count itself thread-safe, but reads and writes
to the stored value are not—callers are responsible for synchronization.
:::
---
## Using pointers
The [`Pointer`](/docs/std/memory/pointer/Pointer/) struct is Mojo's
primary pointer type for indirectly referencing locations in memory.
You can use a pointer in many different ways:
- As a safe, indirect reference to an existing owned value. (For
example, the iterator for a collection might hold a pointer back
to the original collection.)
- As a pointer to a block of dynamically-allocated memory, to build
array-like data structures.
- As a raw memory location to pass to low-level interfaces or other
programming languages.
Some of these uses are safe, but others—particularly those involving
dynamically-allocated memory—are *unsafe*: your code, not the compiler, is
responsible for using the memory correctly.
For a comparison of standard library pointer types, see [Intro to
pointers](/docs/manual/pointers/).
## Pointer basics
A `Pointer` is a type that holds an address to memory. You can store
and retrieve values in that memory. The `Pointer` type is *parameterized*—it can
point to any type of value, and the value type is specified as a parameter. The
value pointed to by a pointer is sometimes called a *pointee*.
```mojo
var count: Int = 0
# Point to an existing value
var ptr = Pointer(to=count) # ptr's type is Pointer[Int, ...]
# Mutate the value
ptr[] = 100
```
![A local variable, ptr, points to a Pointer[Int] holding the address
0x06f6a6f4d. An arrow leads from the Pointer to an Int pointee containing the
value 100, whose memory address is
0x06f6a6f4d.](../images/pointers/pointer-diagram.png#light)
![A local variable, ptr, points to a Pointer[Int] holding the address
0x06f6a6f4d. An arrow leads from the Pointer to an Int pointee containing the
value 100, whose memory address is
0x06f6a6f4d.](../images/pointers/pointer-diagram-dark.png#dark)
Figure 1. Pointer and pointee
Accessing the memory—to retrieve or update a value—is called
*dereferencing* the pointer. You can dereference a pointer by following the
variable name with an empty pair of square brackets:
```mojo
# Update an initialized value
ptr[] += 10
# Access an initialized value
print(ptr[])
```
```output
110
```
These two operations—creating a pointer to an existing value and
dereferencing that pointer—are safe: the pointer maintains the ownership
linkage to the original value, so Mojo can track the memory.
Other operations, especially those involving dynamically-allocated memory, are
generally *unsafe*, meaning that your code is responsible for:
- allocating and deallocating memory
- knowing whether a given memory location is initialized or uninitialized
- manually calling deinitializers when a pointee is no longer being used
Unsafe operations are prefixed with `unsafe_` or use a keyword argument prefixed
with `unsafe_`.
## Lifecycle of a pointer
At any given time, a pointer value can be in one of several states. It can be
*uninitialized*, *dangling*, or point to a valid memory location which is either
initialized or uninitialized:
- Uninitialized. Just like any variable, a variable of type `Pointer` can
be declared but uninitialized.
```mojo no-test
var ptr: Pointer[Int, MutUntrackedOrigin]
```
- Pointing to allocated, uninitialized memory. The
[`alloc()`](/docs/std/memory/alloc/alloc/) function allocates a block of
memory with space for the specified number of elements of the pointee's type,
and [`Allocation.unsafe_ptr()`](/docs/std/memory/alloc/Allocation/#unsafe_ptr)
returns a pointer to that memory.
```mojo
var allocation = alloc(Layout[Int].single())
var ptr = allocation.unsafe_ptr()
```
Trying to dereference a pointer to uninitialized memory results in undefined
behavior.
- Pointing to initialized memory. You can initialize an allocated, uninitialized
pointer by moving or copying an existing value into the memory. Or you can
construct a pointer to an existing value by calling the initializer with the
`to` keyword argument.
```mojo no-test
ptr.unsafe_write(value^)
# or
ptr.unsafe_write(copy=value)
# or
var ptr = Pointer(to=value)
```
Once the value is initialized, you can read or mutate it using the dereference
syntax:
```mojo no-test
var oldValue = ptr[]
ptr[] = newValue
```
- Dangling. When you free the pointer's allocated memory, you're left with a
*dangling pointer*. The address still points to its previous location, but the
memory is no longer allocated to this pointer. Trying to dereference the
pointer, or calling any method that would access the memory location, results
in undefined behavior.
```mojo
dealloc(allocation^)
```
The following diagram shows the lifecycle of a `Pointer`:
![A state diagram with four states. A pointer declared as var ptr:
Pointer[T] starts uninitialized. From there, Allocation.unsafe_leak() or
Allocation.unsafe_ptr() leads to a pointer to uninitialized memory, and
Pointer(to=val) leads to a pointer to initialized memory. unsafe_write()
moves a pointer from uninitialized to initialized memory, while
unsafe_take_pointee() and unsafe_deinit_pointee() move it back. A pointer
to initialized memory can be read or mutated in place. Calling dealloc() on
the Allocation leaves a dangling
pointer.](../images/pointers/pointer-lifecycle.png#light)
![A state diagram with four states. A pointer declared as var ptr:
Pointer[T] starts uninitialized. From there, Allocation.unsafe_leak() or
Allocation.unsafe_ptr() leads to a pointer to uninitialized memory, and
Pointer(to=val) leads to a pointer to initialized memory. unsafe_write()
moves a pointer from uninitialized to initialized memory, while
unsafe_take_pointee() and unsafe_deinit_pointee() move it back. A pointer
to initialized memory can be read or mutated in place. Calling dealloc() on
the Allocation leaves a dangling
pointer.](../images/pointers/pointer-lifecycle-dark.png#dark)
Figure 2. Lifecycle of a Pointer
### Allocating memory
Use the [`std.memory.alloc`](/docs/std/memory/alloc/) module to allocate and
deallocate memory. To allocate memory, you need to provide a *layout*, which
specifies:
- The type of value to be stored (for example, `Int`).
- The number of values to allocate space for.
- Optionally, the memory alignment for the allocation.
The `alloc()` function returns an `Allocation`, an explicitly-destroyed handle
that holds an unsafe pointer to the allocated memory and the layout used to
allocate it. Use `dealloc()` to free the allocation and its associated memory.
```mojo
from std.memory.alloc import alloc, dealloc, Layout
var allocation = alloc(Layout[Int](count=4))
# Use allocation
var ptr = allocation.unsafe_ptr()
for i in range(4):
ptr.unsafe_offset(i).unsafe_write(i)
# Release allocation
dealloc(allocation^)
```
You can also write the allocation above as `alloc[Int]({count = 4})`.
Because `Allocation` is an explicitly-destroyed type, you must deallocate it
before it goes out of scope.
Allocation failure terminates the program; you can't catch this failure with a
`try/except` block. The `alloc()` function always returns an allocation with a
valid, non-null pointer pointing to the allocated space. The allocated space is
*uninitialized*—like a variable that's been declared but not initialized.
### Initializing the pointee
To initialize allocated memory, `Pointer` provides the
[`unsafe_write()`](/docs/std/memory/pointer/Pointer/#unsafe_write)
method, which moves a value into the pointer's memory location:
```mojo no-test
str_ptr.unsafe_write(my_string^)
```
Note that to move the value, you usually need to add the transfer sigil (`^`),
unless the value is an
[implicitly copyable](/docs/std/traits/copyable/ImplicitlyCopyable/) type (like
`Int`) or a newly-constructed, "owned" value:
```mojo
str_ptr.unsafe_write("Owned string")
```
To copy a value into the pointer's memory location instead of moving it, pass
it as the `copy` keyword argument:
```mojo no-test
ptr.unsafe_write(copy=my_value)
```
Alternately, you can get a pointer to an existing value by calling the
`Pointer` initializer with the keyword `to` argument. This is useful for
getting a pointer to a value on the stack, for example.
```mojo
var counter: Int = 5
var ptr = Pointer(to=counter)
```
Note that when calling `Pointer(to=value)`, you don't need to allocate
memory, since you're pointing to an existing value.
### Dereferencing pointers
Use the `[]` dereference operator to access the value stored at a pointer (the
"pointee").
```mojo
# Read from pointee
print(ptr[])
# Mutate pointee
ptr[] = 0
```
```output
5
```
If you've allocated space for multiple values, you can use subscript syntax
with the `unsafe_offset` keyword argument to access the values:
```mojo no-test
ptr[unsafe_offset=3] = 0
# Equivalent to:
ptr.unsafe_offset(3)[] = 0
```
You cannot safely use the dereference operator on uninitialized memory,
even to *initialize* a pointee. This is because assigning to a dereferenced
pointer calls lifecycle methods on the existing pointee (such as the
deinitializer, move initializer or copy initializer).
```mojo
var allocation = alloc[String]({count = 1})
var str_ptr = allocation.unsafe_ptr()
# str_ptr[] = "Testing" # Undefined behavior!
str_ptr.unsafe_write("Testing")
str_ptr[] += " pointers" # Works now
```
### Destroying or removing values
The
[`unsafe_take_pointee()`](/docs/std/memory/pointer/Pointer/#unsafe_take_pointee)
method moves a pointee from the memory location pointed to by `ptr`. This is a
consuming move. It invokes the move initializer on the destination value. It
leaves the memory location uninitialized.
The
[`unsafe_deinit_pointee()`](/docs/std/memory/pointer/Pointer/#unsafe_deinit_pointee)
method calls the deinitializer on the pointee, and leaves the memory location
pointed to by `ptr` uninitialized.
Both `unsafe_take_pointee()` and `unsafe_deinit_pointee()` require that the
pointer is non-null, and the memory location contains a valid, initialized value
of the pointee's type; otherwise the function results in undefined behavior.
Calling
[`unsafe_write_move_from(self, src)`](/docs/std/memory/pointer/Pointer/#unsafe_write_move_from)
moves the value pointed to by `src` into the memory location pointed to by
`self`. After this operation, ownership of that value transfers from `src` to
`self` and the memory at `src` is uninitialized: do not read from it, and do not
invoke deinitializers on it. To make the memory valid again, initialize it with
a new value using one of the `unsafe_write*()` operations.
:::note
Mojo assumes the destination memory is uninitialized.
It does not destroy existing contents before writing the value from `src`.
:::
### Freeing memory
Calling [`dealloc()`](/docs/std/memory/alloc/dealloc/) on an allocation frees
the allocated memory. It doesn't call the deinitializers on any values stored in
the memory. You need to do that explicitly (for example, using
[`unsafe_deinit_pointee()`](/docs/std/memory/pointer/Pointer/#unsafe_deinit_pointee)
or one of the other functions described in
[Destroying or removing values](#destroying-or-removing-values)).
Disposing of a pointer without freeing the associated memory can result in a
memory leak—where your program keeps taking more and more memory, because not
all allocated memory is being freed.
Since deallocating an `Allocation` or `ThinAllocation` consumes the allocation,
you're protected from freeing an allocation twice, unless you use the
`unsafe_leak()` method described in
[Allocations and raising functions](#allocations-and-raising-functions).
After freeing a pointer's memory, you're left with a dangling pointer—its
address still points to the freed memory. Any attempt to access the memory,
like dereferencing the pointer, results in undefined behavior.
## Storing multiple values
As mentioned in [Allocating memory](#allocating-memory), you can use a
`Pointer` to allocate memory for multiple values. The memory is allocated
as a single, contiguous block. The
[`unsafe_offset()`](/docs/std/memory/pointer/Pointer/#unsafe_offset) method
returns a new pointer offset by the specified number of values from the
original pointer:
```mojo
var third_ptr = first_ptr.unsafe_offset(2)
```
The offset can also be negative, to move backward through the block. Because
`unsafe_offset()` returns a new pointer instead of modifying the original, you
assign the result back to a variable to advance it:
```mojo
# Advance the pointer one element:
ptr = ptr.unsafe_offset(1)
```


Figure 3. Pointer offsets
For example, the following code allocates memory to store 6 `Float64`
values, and initializes them all to zero.
```mojo
var allocation = alloc(Layout[Float64](count=6))
var float_ptr = allocation.unsafe_ptr()
for offset in range(6):
float_ptr.unsafe_offset(offset).unsafe_write(0.0)
```
Once the values are initialized, you can access them using subscript syntax
with the `unsafe_offset` keyword argument:
```mojo
float_ptr[unsafe_offset=2] = 3.0
for offset in range(6):
print(float_ptr[unsafe_offset=offset], end=", ")
```
```output
0.0, 0.0, 3.0, 0.0, 0.0, 0.0,
```
## Pointers and origins
The `Pointer` struct has an `origin` parameter to track the origin of the
memory it points to. The full parameter signature for `Pointer` looks like
this:
```mojo no-test
struct Pointer[
mut: Bool,
//,
T: AnyType,
origin: Origin[mut=mut],
*,
address_space: AddressSpace = .GENERIC,
]
```
For pointers initialized with the `to` keyword argument, the origin is inferred
from the origin of the pointee. For example, in the following code,
`s_ptr.origin` is the same as the origin of `s`:
```mojo
var s = "Testing"
var s_ptr = Pointer(to=s)
```
When allocating memory with the `alloc()` function, the returned pointer has an
`origin` value of `MutUntrackedOrigin`. This value represents an origin that is
mutable and doesn't *alias* existing values. For example, it doesn't point to
the memory allocated for any other variable. This memory isn't
tracked by Mojo's lifetime checker and you're responsible for freeing it.
If you're using a pointer in the implementation of a struct, you usually
don't have to worry about the origin, as long as the pointer isn't exposed
outside of the struct. For example, if you implement a static array type
that allocates memory in its initializer, deallocates in its deinitializer,
and doesn't expose the pointer outside of the struct, the default origin is
fine.
But if the struct exposes a pointer or reference to that memory, you need
to set the origin appropriately. For example, the
[`List`](/docs/std/collections/list/List/) type has an `unsafe_ptr()`
method that returns a `Pointer` to the underlying storage. In this case,
the returned pointer should share the origin of the list, since the list is
the logical owner of the storage.
That method looks something like this:
```mojo no-test
def unsafe_ptr[
origin: Origin, address_space: AddressSpace, //
](ref[origin, address_space] self) -> Pointer[
Self.T, origin, address_space=address_space
]:
return (
self._data.unsafe_mut_cast[origin.mut]()
.unsafe_origin_cast[origin]()
.unsafe_address_space_cast[address_space]()
)
```
This returns a copy of the original pointer, with the origin set to match the
origin and mutability of the `self` value.
A method like this is unsafe, but setting the correct origin makes it safer,
since the compiler knows that the pointer is referring to data owned by the
list.
When taking a pointer as a function argument, you often want to require either
a mutable or immutable origin, but otherwise allow the compiler to infer the
origin. Here's an example:
```mojo
def print_bytes(bytes: Pointer[mut=False, Byte, _], count: Int):
for i in range(count):
print(hex(bytes[unsafe_offset=i]), end=" ")
print()
```
By binding the infer-only `mut` parameter to `False`, and leaving the origin
unbound (using `_`), this signature lets the compiler infer the origin, but
forces the origin to be immutable. Mojo can implicitly cast a mutable pointer to
an immutable pointer, so you can pass a mutable pointer into `print_bytes()`,
but the function can't mutate the data.
## Working with nullability
`Pointer` is a non-nullable type. To model a null pointer, wrap it
in [`Optional`](/docs/std/collections/optional/Optional/):
```mojo
var ptr = Optional[Pointer[Int, MutUntrackedOrigin]]()
```
This creates an `Optional` with a value of `None`, which is equivalent
to a null pointer. `Optional[Pointer]` has the same memory layout
as a raw pointer, so you can pass it across FFI boundaries as `NULL`.
To check whether an optional pointer is null, use `Optional` methods:
```mojo
if ptr:
# ptr is not None — safe to unwrap
var p = ptr.value()
```
When you need a non-null value for deferred initialization, use
`unsafe_dangling()` instead of an `Optional`:
```mojo
var ptr = Pointer[Int, MutUntrackedOrigin].unsafe_dangling()
```
For a practical example of optional pointers in a data structure, see
[Self-referential structs](/docs/manual/structs/reference/).
## More memory allocation patterns
In some cases, you may not want to hold on to an `Allocation`:
- When allocating data for a struct, you may want to use a
[`ThinAllocation`](/docs/std/memory/alloc/ThinAllocation/) instead, to avoid
using extra memory.
- When working with raising functions, you sometimes need to avoid
an explicitly-destroyed type like `Allocation` or `ThinAllocation`.
The following sections describe these special cases.
### Holding an allocation in a struct field
When storing an allocation as a struct field, you may not want to store the
extra layout data included in the `Allocation` struct. The layout data is two
`Int` values (alignment and element count), typically an extra 16 bytes per
allocation. If your struct already tracks the amount of space it's allocated,
you can eliminate this extra space by storing a `ThinAllocation`, which is an
explicitly-destroyed wrapper around a pointer.
The `Allocation.into_thin()` method consumes the original allocation and
returns a `ThinAllocation`:
```mojo
struct Counter:
comptime _layout = Layout[Int].single()
var _alloc: ThinAllocation[Int]
def __init__(out self, value: Int):
self._alloc = alloc(Self._layout).into_thin()
self._alloc.unsafe_ptr().unsafe_write(value)
def increment(mut self):
self._alloc.unsafe_ptr()[] += 1
def get(self) -> Int:
return self._alloc.unsafe_ptr()[]
def __deinit__(deinit self):
# Convert ThinAllocation back into Allocation
dealloc(self._alloc^.unsafe_with_layout(Self._layout))
```
To deallocate a `ThinAllocation`, you need to supply the original layout
to reconstruct an `Allocation` using the `unsafe_with_layout()` method.
This example shows storing the layout as a comptime member; for a struct
with a dynamic size, you can reconstruct the original layout:
```mojo no-test
self._alloc^.unsafe_with_layout({count = size})
```
### Allocations and raising functions
Because `Allocation` and `ThinAllocation` need to be explicitly deallocated
before they go out of scope, they can conflict with raising functions.
Consider the following code:
```mojo no-test
def allocating_function() raises:
var data = alloc[Float64]({count = 64})
# ...
raising_function(data.unsafe_ptr())
dealloc(data^)
```
Because an error can cause `allocating_function()` to exit without executing the
`dealloc()` call, the compiler identifies this as a potential memory leak. There
are a couple of approaches to this problem. The function can use a
`try`/`except` statement to ensure that the memory is deallocated in the event
of an error:
```mojo
def allocating_function() raises:
var data = alloc[Float64]({count = 64})
# ...
try:
raising_function(data.unsafe_ptr())
except e:
dealloc(data^)
raise e^ # propagate the error
dealloc(data^)
```
Where this isn't viable, the alternative is to use the
`unsafe_leak()` method to take ownership of the allocation's
pointer. This consumes the allocation, but requires you to
ensure the memory is deallocated. You should consider this
pattern a last resort if other patterns don't work:
```mojo
def leaky_function() raises:
var data_ptr = alloc[Float64]({count = 64}).unsafe_leak()
# ...
raising_function(data_ptr)
dealloc(
ThinAllocation(unsafe_owned_ptr=data_ptr).unsafe_with_layout(
{count = 64}
)
)
```
Downsides of this approach include:
- If `raising_function()` raises an error in this example, `dealloc()` never
gets called, leaking the memory.
- When you reconstruct an `Allocation` from a `Pointer` like this, you run the
risk of freeing the same memory twice.
## Working with foreign pointers
When exchanging data with other programming languages, you may need to construct
a `Pointer` from a foreign pointer. Mojo restricts creating
`Pointer` instances from arbitrary addresses, to avoid users accidentally
creating pointers that *alias* each other (that is, two pointers that refer to
the same location). However, there are specific methods you can use to get a
`Pointer` from a Python or C/C++ pointer.
When dealing with memory allocated elsewhere, you need to be aware of who's
responsible for freeing the memory. Freeing memory allocated elsewhere
can result in undefined behavior.
When working with some foreign functions, you may need to supply a pointer with
no specific type (a type-erased pointer, or "void pointer" in C/C++). This is
equivalent to a Mojo `OpaquePointer`.
You also need to be aware of the format of the data stored in memory, including
data types and byte order. For more information, see
[Converting data: bitcasting and byte order](#converting-data-bitcasting-and-byte-order).
### Creating a Mojo pointer from a raw memory address
You can create a `Pointer` from a raw memory address using the
`unsafe_from_address` initializer.
```mojo
def write_to_address(mmio_address: Int, value: Int32):
var ptr = Pointer[Int32, MutUntrackedOrigin](
unsafe_from_address=mmio_address
)
# Writing to a raw memory address may require a volatile load/store as the
# operation may have side effects not visible to the compiler.
# You can specify this using the `volatile` parameter.
ptr.unsafe_store[volatile=True](value)
```
This is unsafe, as the caller must ensure the address is valid before writing to
it, and that the memory is initialized before reading from it. The caller must
also ensure the pointer's origin and mutability are valid for the address;
failure to do so may result in undefined behavior.
### Creating a Mojo pointer from a Python pointer
The `PythonObject` type defines an
[`unsafe_get_as_pointer()`](/docs/std/python/python_object/PythonObject/#unsafe_get_as_pointer)
method to construct a `Pointer` from a Python address.
:::note
Where possible, use safer methods to exchange data with Python, such as the
[`python.numpy`](/docs/std/python/numpy/) module, which provides convenience
functions for transferring 1D NumPy arrays between Mojo and Python.
:::
The following code creates a NumPy array and then accesses the data
using a Mojo pointer:
```mojo
from std.python import Python
def share_array() raises:
var np = Python.import_module("numpy")
var arr = np.array(Python.list(1, 2, 3, 4, 5, 6, 7, 8, 9))
var ptr = arr.ctypes.data.unsafe_get_as_pointer[.int64]()
for i in range(9):
print(ptr[unsafe_offset=i], end=", ")
print()
def main() raises:
share_array()
```
```output
1, 2, 3, 4, 5, 6, 7, 8, 9,
```
This example uses the NumPy
[`ndarray.ctypes`](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.ctypes.html#numpy.ndarray.ctypes)
attribute to access the raw pointer to the underlying storage
(`ndarray.ctypes.data`). The `unsafe_get_as_pointer()` method constructs a
`Pointer` to this address.
### Working with C/C++ pointers
If you call a C/C++ function that returns a pointer using the
[`external_call`](/docs/std/ffi/external_call/) function, you can
specify the return type as a `Pointer`, and Mojo will handle the type conversion
for you.
Notably, the `origin` parameter when working across FFI boundaries should often
be set to `(Mut/Immut)UntrackedOrigin`, since the pointer points to memory
allocated outside of the Mojo program.
```mojo no-test
from std.ffi import external_call
def get_foreign_pointer() -> Pointer[Int, MutUntrackedOrigin]:
var ptr = external_call[
"my_c_function", # external function name
Pointer[Int, MutUntrackedOrigin] # return type
]()
return ptr
```
### Opaque pointers
The `OpaquePointer` type is a pointer that does not have a specific type. In
other languages, this is usually called a type-erased pointer or a void pointer.
Opaque pointers are usually used when interfacing with non-Mojo code, such as a
C library function that takes a void pointer.
`OpaquePointer` is actually a type alias for `Pointer[NoneType]`, so it
has the same API as any other `Pointer`.
You can't dereference an opaque pointer, but you can cast it to a specific type
using the `unsafe_bitcast()` method. Similarly, you can create an opaque pointer
from an existing pointer by bitcasting to `NoneType`. For example:
```mojo
var str = "Hello, world!"
var str_ptr = Pointer(to=str)
var opaque_ptr = str_ptr.unsafe_bitcast[NoneType]()
# ... call some foreign function that takes a void pointer
```
## Converting data: bitcasting and byte order
Bitcasting a pointer returns a new pointer that has the same memory location,
but a new data type. This can be useful if you need to access different types of
data from a single area of memory. This can happen when you're reading binary
files, like image files, or receiving data over the network.
The following sample processes a format that consists of chunks of data,
where each chunk contains a variable number of 32-bit integers.
Each chunk begins with an 8-bit integer that identifies the number of values
in the chunk.
```mojo
def read_chunks(
var ptr: Pointer[mut=False, UInt8, _],
) -> List[List[UInt32]]:
var chunks = List[List[UInt32]]()
# A chunk size of 0 indicates the end of the data
var chunk_size = Int(ptr[])
while chunk_size > 0:
# Skip the 1 byte chunk_size and get a pointer to the first
# UInt32 in the chunk
var ui32_ptr = ptr.unsafe_offset(1).unsafe_bitcast[UInt32]()
var chunk = List[UInt32](capacity=chunk_size)
for i in range(chunk_size):
chunk.append(ui32_ptr[unsafe_offset=i])
# List is not implicitly copyable, so it needs the transfer sigil (^)
chunks.append(chunk^)
# Move our pointer to the next byte after the current chunk
ptr = ptr.unsafe_offset(1 + 4 * chunk_size)
# Read the size of the next chunk
chunk_size = Int(ptr[])
return chunks^
```
When dealing with data read in from a file or from the network, you may also
need to deal with byte order. Most systems use little-endian byte order (also
called least-significant byte, or LSB) where the least-significant byte in a
multibyte value comes first. For example, the number 1001 can be represented in
hexadecimal as 0x03E9, where E9 is the least-significant byte. Represented as a
16-bit little-endian integer, the two bytes are ordered E9 03. As a 32-bit
integer, it would be represented as E9 03 00 00.
Big-endian or most-significant byte (MSB) ordering is the opposite: in the
32-bit case, 00 00 03 E9. MSB ordering is frequently used in file formats and
when transmitting data over the network. You can use the
[`byte_swap()`](/docs/std/bit/bit/byte_swap/) function to swap the byte
order of a SIMD value from big-endian to little-endian or the reverse. For
example, if the function above were reading big-endian data, you'd need to
change a single line:
```mojo no-test
chunk.append(byte_swap(ui32_ptr[unsafe_offset=i]))
```
## Working with SIMD vectors
The `Pointer` type includes
[`unsafe_load()`](/docs/std/memory/pointer/Pointer/#unsafe_load)
and
[`unsafe_store()`](/docs/std/memory/pointer/Pointer/#unsafe_store)
methods for performing aligned loads and stores of scalar values. It also has
methods supporting strided load/store and gather/scatter.
Strided load loads values from memory into a SIMD vector using an offset (the
"stride") between successive memory addresses. This can be useful for
extracting rows or columns from tabular data, or for extracting individual
values from structured data. For example, consider the data for an RGB image,
where each pixel is made up of three 8-bit values, for red, green, and blue. If
you want to access just the red values, you can use a strided load or store.


Figure 4. Strided load
The following function uses the
[`unsafe_strided_load()`](/docs/std/memory/pointer/Pointer/#unsafe_strided_load)
and
[`unsafe_strided_store()`](/docs/std/memory/pointer/Pointer/#unsafe_strided_store)
methods to invert the red pixel values in an image, 8 values at a time. (Note
that this function only handles images where the number of pixels is evenly
divisible by eight.)
```mojo
def invert_red_channel(ptr: Pointer[mut=True, UInt8, _], pixel_count: Int):
# Number of values loaded or stored at a time
comptime simd_width = 8
# Bytes per pixel, which is also the stride size
comptime bpp = 3
for i in range(0, pixel_count * bpp, simd_width * bpp):
var red_values = ptr.unsafe_offset(i).unsafe_strided_load[
width=simd_width
](bpp)
# Invert values and store them in their original locations
ptr.unsafe_offset(i).unsafe_strided_store[width=simd_width](
~red_values, bpp
)
```
The
[`unsafe_gather()`](/docs/std/memory/pointer/Pointer/#unsafe_gather)
and
[`unsafe_scatter()`](/docs/std/memory/pointer/Pointer/#unsafe_scatter)
methods let you load or store a set of values that are stored in arbitrary
locations. You do this by passing in a SIMD vector of *offsets* to the current
pointer. For example, when using `unsafe_gather()`, the n th value in
the vector is loaded from (pointer address) + offset[n] .
## Safety
To use `Pointer` safely, you need to ensure that the pointer
points to a single, initialized value. If the value is logically
owned by the pointer, you need to ensure the value's deinitializer is
called before deallocating the memory.
Using `Pointer(to=value)` and the simple dereference (`ptr[]`) ensures
that the pointer is as safe as the value it's pointing to.
Using any APIs prefixed with `unsafe_` (or that have keyword arguments
prefixed with `unsafe_`) results in a potentially unsafe operation.
For example:
- If you allocate memory, you need to deallocate the memory. If you use the
`unsafe_leak()` method to obtain a pointer from an allocation, the Mojo
lifetime checker can't track the memory and won't error on possible leaks. You
need to ensure the memory gets deallocated. This is also true if you assume
responsibility for an allocation by calling a method like
`List.unsafe_take_allocation()`.
- If you allocate memory, or take ownership of an allocation from another
source, you need to track whether pointees are initialized or uninitialized.
Accessing uninitialized memory results in undefined behavior.
- When accessing more than one value through a pointer (for example, using
`unsafe_offset()` or `unsafe_load()`), you're always in unsafe territory.
You must track the size of the allocation (to know whether a given address
is valid) and which values are initialized.
---
## Python interoperability
Not only does Mojo use a Pythonic syntax, our plan is to provide full
compatibility with the Python ecosystem. There are two types of compatibility
(or interoperability) that we support:
- [Calling Python from Mojo](/docs/manual/python/python-from-mojo/):
You can import existing Python modules and use them in a Mojo program. This
is 100% compatible because we use the CPython runtime without modification
for full compatibility with existing Python libraries. You can construct
Python objects and call Python functions directly from Mojo, using the
CPython interpreter as a dynamic library (shown as `libpython.dylib` in
figure 1).
- [Calling Mojo from Python](/docs/manual/python/mojo-from-python/):
You can extend your Python code with high-performance Mojo code (or
incrementally migrate Python code to Mojo). Because Mojo is a compiled
language, we can't directly "evaluate" Mojo code from Python. Instead, you
must declare which Mojo functions and types are available to be called from
Python (declare the "bindings"), and then you can import them in your Python
code (shown as `mojo_module` in figure 1) just like any other module—there's
no extra compilation step.
Figure 1. A simplified look at how a Mojo program calls
into Python and a Python program calls into a Mojo module.
By embracing both directions of language interoperability, you can choose
how to use Mojo with Python in a way that works best for your use case.
:::note Python requirement
Mojo itself doesn't require Python. To use the Mojo ↔ Python
interoperability features described in this section, you need
Python 3.10–3.14.
:::
**To learn more about bridging Python ↔ Mojo, continue reading**:
---
## Calling Mojo from Python
If you have an existing Python project that would benefit from Mojo's
high-performance computing, you shouldn't have to rewrite the whole thing in
Mojo. Instead, you can write just the performance-critical parts your code in
Mojo and then call it from Python.
:::experiment Beta feature
Calling Mojo code from Python is in early development. You should expect a lot
of changes to the API and ergonomics. Likewise, this documentation is still a
work in progress. See below for [known limitations](#known-limitations).
:::
## Import a Mojo module in Python
To illustrate what calling Mojo from Python looks like, we'll start with a
simple example, and then dig into the details of how it works and what is
possible today.
Consider a project with the following structure:
```text
project
├── 🐍 main.py
└── 🔥 mojo_module.mojo
```
The main entrypoint is a Python program called `main.py`, and the Mojo code
includes functions to call from Python.
For example, let's say we want a Mojo function to take a Python value as an
argument:
```mojo title="mojo_module.mojo"
def factorial(py_obj: PythonObject) raises -> Python
var n = Int(py=py_obj)
return math.factorial(n)
```
And we want to call it from Python like this:
```python title="main.py"
import mojo_module
print(mojo_module.factorial(5))
```
However, before we can call the Mojo function from Python, we must declare it
so Python knows it exists.
Because Python is trying to load `mojo_module`, it looks for a function called
`PyInit_mojo_module()`. (If our file was called `foo.mojo`, the function Python
looked for would be `PyInit_foo()`.) Within the `PyInit_mojo_module()`, we must
declare all Mojo functions and types that are callable from Python using
[`PythonModuleBuilder`](/docs/std/python/bindings/PythonModuleBuilder/).
So the complete Mojo code looks like this:
```mojo title="mojo_module.mojo"
from std.python import PythonObject
from std.python.bindings import PythonModuleBuilder
from std import math
from std.os import abort
@export
def PyInit_mojo_module() abi("C") -> PythonObject:
try:
var m = PythonModuleBuilder("mojo_module")
m.def_function[factorial]("factorial", docstring="Compute n!")
return m.finalize()
except e:
abort(String("error creating Python Mojo module:", e))
def factorial(py_obj: PythonObject) raises -> PythonObject:
# Raises an exception if `py_obj` is not convertible to a Mojo `Int`.
var n = Int(py=py_obj)
return math.factorial(n)
```
On the Python side, we add the directory containing `mojo_module.mojo` to the
Python path, and then use a normal `import` statement to load our Mojo code:
```python title="main.py"
import mojo.importer
import mojo_module
print(mojo_module.factorial(5))
```
That's it! Try it:
```sh
python main.py
```
```output
120
```
### How it works
Python supports a standard mechanism called [Python extension
modules](https://docs.python.org/3/extending/extending.html) that enables
compiled languages (like Mojo, C, C++, or Rust) to make themselves callable
from Python in an intuitive way. Concretely, a Python extension module is
simply a dynamic library that defines a suitable `PyInit_*()` function.
Mojo comes with built-in functionality for defining Python extension modules.
The special stuff happens in the `mojo.importer` module we imported.
If we have a look at the filesystem after Python imports the Mojo code, we'll
notice there's a new `__mojocache__` directory, with a dynamic library (`.so`)
file inside:
```text
project
├── main.py
├── mojo_module.mojo
└── __mojocache__
└── mojo_module.hash-ABC123.so
```
Loading `mojo.importer` loads our Python Mojo
[import hook](https://docs.python.org/3/reference/import.html#import-hooks),
which behind the scenes looks for a `.mojo` file that matches the imported
module name, and if found, compiles it using
[`mojo build --emit shared-lib`](/docs/cli/build/#--emit-file_type) to generate
a dynamic library. The resulting file is stored in `__mojocache__`, and is
rebuilt only when it becomes stale (typically, when the Mojo source file
changes).
:::note Clearing cached build artifacts
The `__mojocache__` directory should contain only derived artifacts. It is
always safe to delete the contents of a `__mojocache__` directory. Needed
artifacts will simply be rebuilt the next time the Mojo module is imported.
:::
### The `abi` of exported functions
An [`@export`](/docs/reference/decorators/export/) function must declare which
calling convention it uses with an explicit
[`abi`](/docs/reference/function-declarations#abi-c) effect. In a Python
extension module, the only function you need to export is the
`PyInit_` entry point, and it must use `abi("C")`:
```mojo
@export
def PyInit_mojo_module() abi("C") -> PythonObject:
...
```
This is because the CPython runtime locates and calls `PyInit_`
directly across the C boundary, so it must expose the C calling convention. A
`abi("C")` function can't be marked `raises`, which is why the examples above
catch any error inside the body and `abort` instead of propagating it.
The functions, methods, and initializers you register with the module builder
(`def_function`, `def_method`, `def_py_init`, and so on) don't need `@export` at
all; you pass them by reference, and Mojo generates the C wrapper that CPython
actually calls. That wrapper invokes your function using the Mojo calling
convention and translates any raised error into a Python exception, so a
registered function such as `factorial` above can freely be marked `raises`.
Now that we've looked at the basics of how Mojo can be used from Python, let's
dig into the available features and how you can leverage them to accelerate
your Python with Mojo.
## Bindings features
### Binding Mojo types
You can bind any Mojo type for use in Python using
[`PythonModuleBuilder`](/docs/std/python/bindings/PythonModuleBuilder/).
For example:
```mojo
@fieldwise_init
struct Person(Movable, Writable):
var name: String
var age: Int
@export
def PyInit_person_module() abi("C") -> PythonObject:
try:
var mb = PythonModuleBuilder("person_module")
var person_type = mb.add_type[Person]("Person")
except e:
abort("error creating Mojo module")
```
When you call
[`add_type()`](/docs/std/python/bindings/PythonModuleBuilder/#add_type), it
returns a
[`PythonTypeBuilder`](/docs/std/python/bindings/PythonTypeBuilder/), which
you can then use to bind the type constructor (see [binding Python
initializers](#constructing-mojo-objects-in-python), below) and methods.
Any Mojo type bound using a `PythonTypeBuilder` has the resulting Python
'type' object globally registered, enabling two features:
- Constructing Python objects that wrap Mojo values for use from Python using
`PythonObject(alloc=Person(..))`.
- Downcasting using `python_obj.downcast_value_ptr[Person]()`
:::note
Mojo types must implement
[`Writable`](/docs/std/format/Writable/) to be bound for use
in Python. Additional traits are required for specific binding features:
`Movable` for custom initializers (`def_py_init`), and both `Defaultable` and
`Movable` for default initializers (`def_init_defaultable`).
:::
However, merely binding a Mojo type to a Python `type` object isn't very useful
on its own. Next, we'll tell Python how to interact with our Mojo type—starting
with how to construct instances of our Mojo type from within Python.
### Constructing Mojo objects in Python
Mojo types can be constructed from Python by declaring a Mojo initializer
function as a Python-compatible object initializer using
[`def_py_init()`](/docs/std/python/bindings/PythonTypeBuilder/#def_py_init) when
you add the type to your module. For example:
```mojo
@export
def PyInit_person_module() abi("C") -> PythonObject:
try:
var mb = PythonModuleBuilder("person_module")
# highlight-start
_ = mb.add_type[Person]("Person").def_py_init[Person.py_init]()
# highlight-end
return mb.finalize()
except e:
abort(String("error creating Python Mojo module:", e))
@fieldwise_init
struct Person(Movable, Writable):
var name: String
var age: Int
# highlight-start
@staticmethod
def py_init(
out self: Person, args: PythonObject, kwargs: PythonObject
) raises:
# Validate argument count
if len(args) != 2:
raise Error("Person() takes exactly 2 arguments")
# Convert Python arguments to Mojo types
var name = String(args[0])
var age = Int(args[1])
self = Self(name, age)
# highlight-end
```
With this Mojo binding, you can create `Person` instances in Python:
```python
person = person_module.Person("Sarah", 32)
print(person)
```
```output
Person(name=Sarah, age=32)
```
For types that support default construction, you can use the simpler
[`def_init_defaultable()`](/docs/std/python/bindings/PythonTypeBuilder/#def_init_defaultable)
method:
```mojo
var counter_type = m.add_type[Counter]("Counter")
counter_type.def_init_defaultable[Counter]()
```
This enables Python code to create instances without arguments:
```python
counter = counter_module.Counter() # Creates Counter()
```
:::note "Constructor" vs "Initializer"
In Python, object construction happens across both the `__new__()` and
`__init__()` methods, so the `__init__()` method is technically just the
attribute initializer. However, in a Mojo struct, there's no `__new__()`
method, so we prefer to always call `__init__()` the initializer.
:::
### Returning Mojo objects to Python
Mojo functions called from Python don't just need to be able to accept
[`PythonObject`](/docs/std/python/python_object/PythonObject/) values as
arguments, they also need to be able to return new values. And sometimes, they
even need to be able to return Mojo native values back to Python. This is
possible by using the `PythonObject(alloc=)` constructor.
An example of this looks like:
```mojo
def create_person() -> PythonObject:
var person = Person("Sarah", 32)
return PythonObject(alloc=person^)
```
:::caution
`PythonObject(alloc=...)` will raise an exception if the provided Mojo object
type had not previously been registered using
[`PythonModuleBuilder.add_type()`](/docs/std/python/bindings/PythonModuleBuilder/#add_type).
:::
### `PythonObject` to Mojo values
Within any Mojo code that is handling a
[`PythonObject`](/docs/std/python/python_object/PythonObject/), but
especially within Mojo functions called from Python, it's common to expect an
argument of a particular type.
There are two ways in which a `PythonObject` can be turned into a native
Mojo value:
- **Converting** a Python object into a newly constructed Mojo value that has
the same logical value as the original Python object.
This is handled by the [`ConvertibleFromPython`][ConvertibleFromPython] trait.
- **Downcasting** a Python object that holds a native Mojo value to a pointer
to that inner value.
This is handled by [`PythonObject.downcast_value_ptr()`][downcast_value_ptr].
#### `PythonObject` conversions
Many Mojo types support conversion directly from equivalent Python types, via
the [`ConvertibleFromPython`][ConvertibleFromPython] trait:
```mojo
# Given a person, clone them and give them a different name.
def create_person(
name_obj: PythonObject,
age_obj: PythonObject
) raises -> PythonObject:
# These conversions will raise an exception if they fail
var name = String(name_obj)
var age = Int(age_obj)
return PythonObject(alloc=Person(name, age))
```
Which could be called from Python using:
```python
person = mojo_module.create_person("John Smith")
```
Passing invalid arguments will result in a runtime argument error:
```python
person = mojo_module.create_person(42)
```
#### `PythonObject` downcasts
Downcasting from `PythonObject` values to the inner Mojo value:
```mojo
def print_age(person_obj: PythonObject) raises:
# Raises if `obj` does not contain an instance of the Mojo `Person` type.
var person = person_obj.downcast_value_ptr[Person]()
print("Person is", person[].age, "years old")
```
Unsafe mutation via downcasting is also supported. It is up to the user to
ensure that this mutable pointer does not alias any other pointers to the same
object within Mojo:
```mojo
def birthday(person_obj: PythonObject):
var person = person_obj.downcast_value_ptr[Person]()
person[].age += 1
```
Entirely unchecked downcasting—which does no type checking—can be done using:
```mojo
def get_person(person_obj: PythonObject):
var person = person_obj.unchecked_downcast_value_ptr[Person]()
```
Unchecked downcasting can be used to eliminate overhead when optimizing a tight
inner loop with Mojo, and you've benchmarked and measured that type checking
downcasts is a significant bottleneck.
{/**/}
### Methods
When binding Mojo objects for use from Python, you can expose chosen methods to
Python as well, using
[`PythonTypeBuilder.def_method()`](/docs/std/python/bindings/PythonTypeBuilder/#def_method).
Currently, Mojo methods being exposed to Python must be written with a
modification compared to normal Mojo methods: they must be a `@staticmethod`
that takes either `py_self: PythonObject` or `self_ptr: Pointer[Self]`:
```mojo
from std.python import PythonObject
from std.python.bindings import PythonModuleBuilder
from std.os import abort
@export
def PyInit_mojo_module() abi("C") -> PythonObject:
try:
var mb = PythonModuleBuilder("mojo_module")
# highlight-start
_ = mb.add_type[Person]("Person")
.def_method[Person.get_name]("get_name")
.def_method[Person.set_age]("set_age")
# highlight-end
return mb.finalize()
except e:
abort("error creating Mojo module")
struct Person(Writable):
var name: String
var age: Int
# highlight-start
@staticmethod
def get_name(py_self: PythonObject) raises -> PythonObject:
var self_ptr = py_self.downcast_value_ptr[Self]()
return self_ptr[].name
@staticmethod
def set_age(
self_ptr: Pointer[mut=True, Self],
new_age: PythonObject,
) raises:
self_ptr[].age = Int(new_age)
# highlight-end
def write_to(self, mut writer: Some[Writer]):
t"Person({self.name}, {self.age})".write_to(writer)
```
Taking `py_self: PythonObject` allows access to the full `PythonObject`
allocation that a Mojo object instance is stored inside of. Typically though,
taking `self_ptr: Pointer[Self]` will minimize boilerplate in the common
case that a method merely needs to access the fields of an object.
Mojo methods called from Python are currently required to take non-standard self
types due to limitations that will be lifted in future versions of Python Mojo
bindings.
### Static methods
Python Mojo bindings supports exposing Python `@staticmethods`, bound using
[`PythonTypeBuilder.def_staticmethod()`](/docs/std/python/bindings/PythonTypeBuilder/#def_staticmethod).
A function declared using `def_staticmethod()` is callable as a static method on
the type within Python, without needing an object instance.
```mojo
from std.python import PythonObject
from std.python.bindings import PythonModuleBuilder
from std.os import abort
@export
def PyInit_mojo_module() abi("C") -> PythonObject:
try:
var mb = PythonModuleBuilder("mojo_module")
# highlight-start
mb.add_type[Person]("Person")
.def_staticmethod[Person.is_valid_age]("is_valid_age")
# highlight-end
return mb.finalize()
except e:
abort("error creating Mojo module")
struct Person(Writable):
var name: String
var age: Int
# highlight-start
@staticmethod
def is_valid_age(age_obj: PythonObject) raises -> PythonObject:
var age = Int(age_obj)
return 0 <= age <= 130
# highlight-end
def write_to(self, mut writer: Some[Writer]):
t"Person({self.name}, {self.age})".write_to(writer)
```
Calling a Mojo function bound as a static method looks like a typical Python
static method call directly on the type object:
```python title="main.py"
from mojo_module import Person
print(Person.is_valid_age(45)) # Prints 'True'
print(Person.is_valid_age(-1)) # Prints 'False'
```
### Keyword arguments
[Keyword arguments in Mojo](/docs/manual/functions/#keyword-arguments) come in
two forms:
1. Keyword-only arguments: `def foo(*, x: Int)`
This is not currently supported in Python Mojo bindings.
2. [Variadic keyword arguments](/docs/manual/functions/#variadic-keyword-arguments):
`def foo(var **kwargs: Int)` This is supported in Python Mojo bindings when
used in the unsugared form: `def foo(kwargs: StringDict)`. (The `**kwargs`
syntax limitation will be removed in the future.)
You can define Mojo functions that accept variadic keyword arguments using
[`StringDict[PythonObject]`](/docs/std/collections/dict/StringDict/)
as the last argument. A simple example looks like:
```python
import mojo_module
result = mojo_module.sum_kwargs_ints(a=10, b=20, c=30) # returns 60
```
```mojo
from std.collections import StringDict
def sum_kwargs_ints(kwargs: StringDict[PythonObject]) raises -> PythonObject:
var total = 0
for entry in kwargs.items():
total += Int(entry.value)
return PythonObject(total)
```
Keyword arguments are also supported following normal positional arguments.
Additionally, getting specific keyword arguments is a dictionary lookup on the
`StringDict`:
```mojo
from std.collections import StringDict
def duration_in_seconds(
hours_obj: PythonObject,
minutes_obj: PythonObject,
kwargs: StringDict[PythonObject]
) raises -> PythonObject:
var hours = Int(hours_obj)
var minutes = Int(minutes_obj)
var seconds = Int(kwargs["seconds"])
return hours * 3600 + minutes * 60 + seconds
```
In this example, if a call to `duration_in_seconds()` is missing the required
`"seconds"` named argument, a runtime exception will occur:
```python title="main.py"
from mojo_module import duration_in_seconds
# Pass hours and minutes, missing "seconds"
duration_in_seconds(4, 5) # ERROR: KeyError
```
Keyword arguments are supported when bindings top-level functions, methods, and
static methods.
### Variadic arguments
Python and Mojo variadic arguments are normally written using the following
syntax:
```mojo
def foo(*args: Int):
...
```
However, this syntax is not yet supported in Python/Mojo bindings, because
functions bound using
[`def_function()`](/docs/std/python/bindings/PythonModuleBuilder/#def_function)
support only fixed-arity functions.
As a workaround, you can expose Mojo functions that accept a variadic number of
arguments to Python using the lower-level
[`def_py_function()`](/docs/std/python/bindings/PythonModuleBuilder/#def_py_function)
interface, which leaves it to the user to validate the number of arguments
provided:
```mojo
@export
def PyInit_mojo_module() abi("C") -> PythonObject:
try:
var b = PythonModuleBuilder("mojo_module")
b.def_py_function[count_args]("count_args")
b.def_py_function[sum_args]("sum_args")
b.def_py_function[lookup]("lookup")
def count_args(py_self: PythonObject, args_tuple: PythonObject) raises:
return len(args_tuple)
def sum_args(py_self: PythonObject, args_tuple: PythonObject) raises:
var total = args_tuple[0]
for i in range(1, len(args_tuple)):
total += args_tuple[i]
return total
def lookup(py_self: PythonObject, args_tuple: PythonObject) raises:
if len(args_tuple) != 2 and len(args_tuple) != 3:
raise Error("lookup() expects 2 or 3 arguments")
var collection = args_tuple[0]
var key = args_tuple[1]
try:
return collection[key]
except e:
if len(args) == 3:
return args_tuple[2]
else:
raise e
```
## Strategies for porting Python to Mojo
### Writing Pythonic code in Mojo
In this approach to bindings, we embrace the flexibility of Python, and eschew
trying to convert `PythonObject` arguments into the narrowly constrained,
strongly-typed space of the Mojo type system, in favor of just writing some code
and letting it raise an exception at runtime if we got something wrong.
The flexibility of `PythonObject` enables a unique programming style, wherein
Python code can be "ported" to Mojo with relatively few changes.
```python
def foo(x, y, z):
x[y] = int(z)
x = y + z
```
Rule of thumb: Any Python builtin function should be accessible in Mojo using
`Python.()`.
```mojo
def foo(x: PythonObject, y: PythonObject, z: PythonObject) -> PythonObject:
x[y] = Python.int(z)
x = y + z
```
## Building Mojo extension modules
You can create and distribute your Mojo modules for Python in the following
ways:
- As source files, compiled on demand using the Python Mojo importer hook.
The advantage of this approach is that it's easy to get started with, and
keeps your project structure simple, while ensuring that your imported Mojo
code is always up to date after you make an edit.
- As pre-built Python extension module `.so` dynamic libraries, compiled using:
```bash
mojo build mojo_module.mojo --emit shared-lib -o mojo_module.so
```
This has the advantage that you can specify any other necessary build options
manually (optimization or debug flags, import paths, etc.), providing an
"escape hatch" from the Mojo import hook abstraction for advanced users.
## Known limitations
While we have big ambitions for Python to Mojo interoperability—our goal is for
Mojo to be the best way to extend Python—this feature is still in early and
active development, and there are some limitations to be aware of. These will
be lifted over time.
- **Keyword arguments syntax.**
Currently, Mojo functions called from Python only accept keyword arguments
when using a trailing `kwargs: StringDict[PythonObject]` argument.
Support for native `**kwargs` syntax will be added in the future.
- **Mojo package dependencies.**
Mojo code that has dependencies on packages other than the Mojo stdlib
(like those in the ever-growing
[Modular Community](https://github.com/modular/modular-community) package
channel) are currently only supported when building Mojo extension modules
manually, as the Mojo import hook does not currently support a way to
specify import paths for Mojo package dependencies.
- **Properties.**
Computed properties getter and setters are not currently supported.
- **Expected type conversions.**
A handful of Mojo standard library types can be constructed directly from
equivalent Python builtin object types, by implementing the
[`ConvertibleFromPython`][ConvertibleFromPython] trait.
However, many Mojo standard library types do not yet implement this trait,
so may require manual conversion logic if needed.
[ConvertibleFromPython]: /docs/std/python/conversions/ConvertibleFromPython/
[downcast_value_ptr]: /docs/std/python/python_object/PythonObject#downcast_value_ptr
---
## Calling Python from Mojo
The Python ecosystem is full of useful libraries, so you shouldn't have to
rewrite them in Mojo. Instead, you can simply import Python packages and call
Python APIs from Mojo. The Python code runs in a standard Python interpreter
(CPython), so your existing Python code doesn't need to change.
## Specify your Python version
Mojo doesn't include a CPython interpreter—it uses the CPython interpreter
provided by your environment's default Python version. So be sure you know
which Python version you're using in each environment where your Mojo code will
run.
To ensure you get consistent results, we recommend you
[use Pixi](https://pixi.prefix.dev/latest/installation/) to manage your package
dependency and virtual environment. In a Pixi project, you can specify the
Python version like this:
```sh
pixi add "python==3.11"
```
Now, even if your operating system's default Python version is something else,
your Pixi project (and the Mojo code inside) always uses Python 3.11.
```sh
pixi run python --version
```
```output
Python 3.11.0
```
## Import a Python module in Mojo
To import a Python module in Mojo, just call
[`Python.import_module()`](/docs/std/python/python/Python/#import_module)
with the module name. The following shows an example of importing the standard
Python [NumPy](https://numpy.org/) package:
```mojo
from std.python import Python
def main() raises:
# This is equivalent to Python's `import numpy as np`
var np = Python.import_module("numpy")
# Now use numpy as if writing in Python
var array = np.array(Python.list(1, 2, 3))
print(array) # [1 2 3]
```
Assuming that you have the NumPy package installed in your environment, this
imports NumPy and you can use any of its features.
If you want to use Python builtin APIs, you just need to import the `builtins`
module the same way. For example:
```mojo
from std.python import Python
def main() raises:
var np = Python.import_module("numpy")
var array = np.array(Python.list(1, 2, 3))
var builtins = Python.import_module("builtins")
print(builtins.type(array)) #
```
A few things to note:
- The `import_module()` method returns a reference to the module in the form of
a [`PythonObject`](/docs/std/python/python_object/PythonObject/) wrapper. You
must store the reference in a variable and then use it as shown in the example
above to access functions, classes, and other objects defined by the module.
See [Mojo wrapper objects](/docs/manual/python/types/#mojo-wrapper-objects)
for more information about the `PythonObject` type.
- Currently, you cannot import individual members (such as a single Python class
or function). You must import the whole Python module and then access members
through the module name.
- Mojo doesn't yet support top-level code, so the `import_module()` call must
be inside another method. This means you may need to import a module multiple
times or pass around a reference to the module. This works the same way as
Python: importing the module multiple times won't run the initialization
logic more than once, so you don't pay any performance penalty.
- `import_module()` may raise an exception. Raising exceptions is much
more common in Python code than in the Mojo standard library, which
[limits their use for performance
reasons](/docs/roadmap#the-standard-library-has-limited-exceptions-use).
- We recommend using a package manager such as pixi, uv, or conda to manage your
environment. For instructions on setting up a Mojo project with pixi, see
[Create a Mojo project](/docs/manual/get-started/#1-create-a-mojo-project) in
the Get started with Mojo tutorial.
:::caution
[`mojo build`](/docs/cli/build/) doesn't include the Python packages used by
your Mojo project. Instead, Mojo loads the Python interpreter and Python
packages at runtime, so they must be provided in the environment where you run
the Mojo program (such as inside the pixi environment where you built the
executable).
:::
### Import a local Python module
If you have some local Python code you want to use in Mojo, just add
the directory to the Python path and then import the module.
For example, suppose you have a Python file named `mypython.py`:
```python title="mypython.py"
import numpy as np
def gen_random_values(size, base):
# generate a size x size array of random numbers between base and base+1
random_array = np.random.rand(size, size)
return random_array + base
```
Here's how you can import it and use it in a Mojo file:
```mojo title="main.mojo"
from std.python import Python
def main() raises:
Python.add_to_path("path/to/module")
var mypython = Python.import_module("mypython")
var values = mypython.gen_random_values(2, 3)
print(values)
```
Both absolute and relative paths work with
[`add_to_path()`](/docs/std/python/python/Python/#add_to_path). For example,
you can import from the local directory like this:
```mojo
Python.add_to_path(".")
```
---
## Python types
When calling Python methods, Mojo needs to convert back and forth between native
Python objects and native Mojo objects. Most of these conversions happen
automatically, but there are a number of cases that Mojo doesn't handle yet.
In these cases you may need to do an explicit conversion, or call an extra
method.
## Mojo types in Python
Mojo primitive types implicitly convert into Python objects. Today we support
integers, floats, booleans, and strings.
To demonstrate, the following example dynamically creates an in-memory Python
module named `py_utils` containing a `type_printer()` function, which simply
prints the type of a given value. Then you can see how different Mojo values
convert into corresponding Python types.
```mojo
from std.python import Python
def main() raises:
var py_module = """
def type_printer(value):
print(type(value))
"""
var py_utils = Python.evaluate(py_module, file=True, name="py_utils")
py_utils.type_printer(4)
py_utils.type_printer(3.14)
py_utils.type_printer(True)
py_utils.type_printer("Mojo")
```
```output
```
## Python types in Mojo
You can also create and use Python objects from Mojo.
### Mojo wrapper objects
When you use Python objects in your Mojo code, Mojo adds the
[`PythonObject`](/docs/std/python/python_object/PythonObject/) wrapper around
the Python object. This object exposes a number of common double underscore
methods (dunder methods) like `__getitem__()` and `__getattr__()`, passing them
through to the underlying Python object. Most of the time, you can treat the
wrapped object just like you'd treat it in Python. You can use dot-notation to
access attributes and call methods, and use the `[]` operator to access an item
in a sequence.
You can explicitly create a wrapped Python object by initializing a
`PythonObject` with a Mojo integer, float, boolean, or string. Additionally, you
can create several types of Python collections directly in Mojo using the
[`Python.dict()`](/docs/std/python/python/Python/#dict),
[`Python.list()`](/docs/std/python/python/Python/#list), and
[`Python.tuple()`](/docs/std/python/python/Python/#tuple) static methods.
For example, to create a Python dictionary, use the
[`Python.dict()`](/docs/std/python/python/Python/#dict) method:
```mojo
from std.python import Python
def main() raises:
var py_dict = Python.dict()
py_dict["item_name"] = "whizbang"
py_dict["price"] = 11.75
py_dict["inventory"] = 100
print(py_dict)
```
```output
{'item_name': 'whizbang', 'price': 11.75, 'inventory': 100}
```
With the [`Python.list()`](/docs/std/python/python/Python/#list) method, you
can create a Python list and optionally initialize it:
```mojo
from std.python import Python
def main() raises:
var py_list = Python.list("cat", 2, 3.14159, 4)
var n = py_list[2]
print("n =", n)
py_list.append(5)
py_list[0] = "aardvark"
print(py_list)
```
```output
n = 3.14159
['aardvark', 2, 3.14159, 4, 5]
```
The [`Python.tuple()`](/docs/std/python/python/Python/#tuple) method creates a
Python tuple of values:
```mojo
from std.python import Python
def main() raises:
var py_tuple = Python.tuple("cat", 2, 3.1415, "cat")
var n = py_tuple[2]
print("n =", n)
print("Number of cats:", py_tuple.count("cat"))
```
```output
n = 3.1415
Number of cats: 2
```
If you want to construct a Python type that doesn't have a literal Mojo
equivalent, you can also use the
[`Python.evaluate()`](/docs/std/python/python/Python/#evaluate) method. For
example, to create a Python `set`:
```mojo
from std.python import Python
def main() raises:
var py_set = Python.evaluate('{2, 3, 2, 7, 11, 3}')
var num_items = len(py_set)
print(num_items, "items in the set.")
var contained = 7 in py_set
print("Is 7 in the set:", contained)
```
```output
4 items in the set.
Is 7 in the set: True
```
`PythonObject` implements the [`Writable`](/docs/std/format/Writable/)
trait. This allows you to print Python values using the built-in
[`print()`](/docs/std/io/io/print/) function, as shown in several of the
previous examples.
However, most other Mojo APIs don't accept `PythonObject` values directly. In
these cases you'll need to explicitly convert a Python value into a native Mojo
value. For example:
```mojo
from std.python import Python
from std.python import PythonObject
def main() raises:
var py_string = PythonObject("Hello, Mojo!")
var py_bool = PythonObject(True)
var py_int = PythonObject(123)
var py_float = PythonObject(3.14)
var mojo_string = String(py=py_string)
var mojo_bool = Bool(py=py_bool)
var mojo_int = Int(py=py_int)
var mojo_float = Float64(py=py_float)
```
### Comparing Python types in Mojo
You can use Python objects in Mojo comparison expressions, and the Mojo `is`
operator also works to compare the identity of two Python objects. Python values
like `False` and `None` evaluate as false in Mojo boolean expressions as well.
If you need to know the type of the underlying Python object, you can use the
[`Python.type()`](/docs/std/python/python/Python/#type) method, which is
equivalent to the Python `type()` builtin. You can test if a Python
object is of a particular type by performing an identity comparison against the
type as shown below:
```mojo
from std.python import Python
def main() raises:
var value1 = PythonObject(3.7)
var value2 = Python.evaluate("10/3")
# Compare values
print("Is value1 greater than 3:", value1 > 3)
print("Is value1 greater than value2:", value1 > value2)
# Compare identities
var value3 = value2
print("value1 is value2:", value1 is value2)
print("value2 is value3:", value2 is value3)
# Compare types
var py_float_type = Python.evaluate("float")
print("Python float type:", py_float_type)
print("value1 type:", Python.type(value1))
print("Is value1 a Python float:", Python.type(value1) is py_float_type)
```
```output
Is value1 greater than 3: True
Is value1 greater than value2: True
value1 is value2: False
value2 is value3: True
Python float type:
value1 type:
Is value1 a Python float: True
```
---
## Mojo tips for Python devs
Mojo is designed with Python programmers in mind, but it isn't "just Python,
only faster." Mojo introduces a type system, ownership-aware semantics,
and low-level control that, as a Python developer, you may not have had
to reason about to make your code work.
This guide offers a practical resource for Python developers.
It shows how familiar Python patterns translate into Mojo, where your
instincts still apply and where Mojo asks you to build a new mental
model.
This isn't a tutorial. It's a core set of language migration tips and
patterns to support you as you migrate to Mojo.
## Mojo's core model for Python developers
Mojo looks like Python but its execution model is closer to Rust,
Swift, C++, and other systems languages. Key differences
from Python include:
- **Mojo is statically typed.** In Python, types are optional hints that
the interpreter _mostly_ ignores at runtime. In Mojo, types are
first-class. The compiler uses them to generate fast, specialized
machine code.
- **Mojo compiles to machine code.** Python runs through an interpreter
that translates your code at runtime, adding overhead to every
operation. Mojo compiles directly to native machine code. This gives
you fast and predictable performance, with no interpreter overhead.
- **Mojo prefers _value semantics_ and explicit mutability.** In Python,
most objects are mutable references, so assigning a list doesn't copy
it. In Mojo, assigning or passing a value typically creates an
independent copy. Changes to one value don't affect others unless you
make sharing explicit. This keeps data flow clear and enables safe
parallelism.
- **Mojo supports modern ownership, but it doesn't trap you in "safe-only"
abstractions.** With ownership, the compiler tracks which variables and fields
control a value's lifetime. That lets Mojo manage memory effectively, without
a garbage collector or reference counting. When you need low-level control,
such as interfacing with C-language libraries, you can manage memory
explicitly using `alloc()`, `dealloc()` and `Pointer`.
- **Mojo brings together ideas from Rust, C++, and Python.** It combines
Python's readability with a performance model inspired by systems
languages like Rust and C++.
Understanding these differences is essential for writing correct, fast
Mojo.
## Moving from Python to Mojo
This section introduces a curated set of migration topics that
explore common Mojo patterns.
### Value semantics
In Mojo, when you assign a value to a new variable, it's given a
unique owned value, not a second reference pointing to the same data.
This can catch new adopters off guard.
In Python, both `a` and `b` refer to the same list:
```python
a = [1, 2, 3]
b = a
b.append(4)
print(a) # [1, 2, 3, 4]
# a changed because b and a both point to the same list
```
In Mojo, assignment gives you a copy. If you're not working with
trivial types (like `Int` or `Bool`) or types with built-in copy
semantics (like `String`), you may need to use an explicit
copy call. You can also use and create types that are implicitly
copyable.
```mojo
var a = "hello" # hello
var b = a # hello, implicit copy
b = b + " world" # hello world
print(a) # hello
print(b) # hello world
var c: List[Int] = [1, 2, 3]
var d = c.copy() # d is an independent copy
d.append(4) # [1, 2, 3, 4]
print(c) # [1, 2, 3] c is unchanged
```
Mojo uses `var` to declare variables. A `var` binding owns its value.
Using `var` consistently makes your code easier to read. It's clear when
you introduce a new binding and when you reassign an existing one.
To use Python-like reference behavior, declare `b` with `ref`
instead of `var`:
```mojo
var a: List[Int] = [1, 2, 3]
ref b = a # b is a reference to the same value
b.append(4) # The list updates. a still owns the list.
print(a) # [1, 2, 3, 4]
```
### Mutability
In both Python and Mojo, almost everything you create can be changed
after the fact. In Mojo, there are some special rules.
**Python**:
Nearly everything is mutable.
```python
a = 10 # 10
b = a # 10
b = b + 10 # 20
print(a, b) # 10 20
```
**Mojo**:
All variables are mutable by default. Function arguments aren't
mutable by default.
```mojo
var x = 10 # 10
x = 20 # 20, with a warning that x's previous assignment
# to 10 was never used
def foo(value: Int):
value += 1 # Error, expression must be mutable
var y = 20
foo(y)
```
Default immutability in the function gives the compiler more room to
optimize. Using the `mut` keyword in the argument declaration makes
it mutable.
```mojo
def foo(mut value: Int):
value += 1 # This works
```
Explicit mutability makes code easier to reason about because mutability is
visible. You can look at a function signature and immediately see which
values can change and which can't.
## Numbers
Python gives you `int` and `float` with arbitrary precision. Mojo takes a
different approach: it uses explicit, fixed-width numeric types so the
compiler can optimize aggressively and scale across parallel execution.
Mojo provides concrete numeric types like `Int`, `Int8`, `Int16`,
`Int32`, `Int64`, `Float16`, `Float32`, and `Float64`. Fixed width isn't a
limitation in Mojo, it's a feature that lets the compiler pack numbers with
known sizes into memory.
### SIMD types
In many languages, SIMD shows up later as a specialized tool for advanced
users. In Mojo, SIMD is part of the core compute model. Mojo implements its
primitive numeric types as SIMD values under the hood.
That lets the compiler operate on multiple values with a single hardware
instruction. When you choose the right numeric type, you give the
compiler more room to generate faster code.
### Int types
**Python**:
Python's `int` uses arbitrary precision. It can hold integers as large as
memory allows.
**Mojo's `Int` type**:
The general `Int` type maps to your machine's native word size. This is
typically 64 bits on a 64-bit system. You can always check:
```mojo
from std.sys import size_of
def main():
var a: Int = 5
var bytes = size_of[Int]()
print(bytes) # 8 on a 64-bit system
```
### Floating point types
**Python**:
In Python, `float` is always 64-bit.
**Mojo**:
Mojo floating point types _aren't_ arbitrary precision.
Mojo doesn't provide a default floating point type, the way it does
with integers. That means there's no built-in `Float` type.
When you're just starting with Mojo, stick to `Float32` or `Float64`
floating point.
### Division and types
Mojo division behaves differently than in Python.
In Python, dividing two integers always produces a float:
```python
a = 7
b = 2
print(7 / 2) # 3.5 — always float
```
In Mojo, if you want integer division to return a floating-point result,
you must use explicit casting:
```mojo
var a: Int = 7
var b: Int = 2
print(Float64(a) / Float64(b)) # 3.5 — explicit float division
```
### Mojo division operators
In Mojo, `/` returns a value that always matches the type of the
operands. A floating point number divided by a floating point number
returns a floating point number, and an integer divided by an integer
returns an integer:
```mojo
var a: Int = 7
var b: Int = 2
print(a / b) # 3, result type matches operand type
```
Python programmers may be a bit surprised that / isn't "true division."
It returns a truncated result, but the result is biased towards zero:
```mojo
var c = -7
var d = 2
print(c / d) # -3, not -4, truncates towards zero
```
`//` performs floored division, in the direction of negative
infinity:
```mojo
print(c // d) # -4, not -3, truncates towards negative infinity
```
Like `/`, the type returned by `//` is preserved from the operands:
```mojo
var e: Float64 = 7.0
var f: Float64 = 2.0
print(e // f) # 3.0, floors toward negative infinity
print(e / f) # 3.5
var g: Float64 = -7.0
print(g // f) # -4.0, floors toward negative infinity
print(g / f) # -3.5
```
## Data structures: lists
**Python**:
In Python, lists are dynamic. They can hold mixed types and grow freely.
```python
nums = ["one", 2.0, 3]
nums.append(4) # ['one', 2.0, 3, 4]
```
**Mojo**:
A typed list holds only one element type. In this example, that type is Int.
This allows the list implementation to pack data efficiently, using less space
and improving performance. Mojo uses packed data rather than indirect
references:
```mojo
var nums: List[Int] = [1, 2, 3]
nums.append(4) # [1, 2, 3, 4]
```
To store different kinds of values, define the element type as a `Variant`
that enumerates permitted types. Variant lets a single element type represent
multiple concrete value types:
```mojo
from std.utils import Variant
comptime MixedType = Variant[Int, Float64, String, Bool]
var mixed_list = List[MixedType]()
mixed_list.append(MixedType(42))
mixed_list.append(MixedType(3.14))
mixed_list.append(MixedType("hello"))
mixed_list.append(MixedType(True))
for item in mixed_list:
print(item) # Output lines: 42, 3.14, hello, and True
```
`Variant` tells the Mojo compiler which types are used, so it can allocate and
manage memory correctly.
## Data structures: dictionaries
Python dicts are dynamic. Keys and values can be anything. Mojo uses
efficient dictionary implementations (Swiss tables) for fast data storage and
retrieval. Typed dictionaries support efficient packing and data access.
**Python**:
```python
counts = {"a": 1, "b": "two"}
counts["c"] = 3.0 # {'a': 1, 'b': 'two', 'c': 3.0}
```
**Mojo**:
A typed declaration like `Dict[String, Int]` tells the compiler exactly
what element types to expect for keys and values. This enables tighter,
faster code. As with other Mojo collections, you can use `Variant` to
broaden the range of permitted element types:
```mojo
var counts: Dict[String, Int] = {"a": 1, "b": 2}
counts["c"] = 3 # {a: 1, b: 2, c: 3}
```
## Comprehensions
Python's comprehensions have direct Mojo analogs. The syntax is
essentially identical.
**Mojo**:
```mojo
var list_squares = [x * x for x in [0, 1, 2, 3, 4] if x % 2 == 0]
# [0, 4, 16], list
var positive_numbers = [x for x in range(-3, 3) if x > 0]
# [1, 2], list
var dict_squares = {x: x * x for x in range(3)}
# {0: 0, 1: 1, 2: 4}, dict
var upper_case = {k: v.upper() for k, v in [(1, "one"), (2, "two")]}
# {1: ONE, 2: TWO}, dict
var number_set = {x for x in range(5)}
# {0, 1, 2, 3, 4}, set
```
## Iteration
In terms of syntax, Mojo's `for` and `while` loops align with
Python. Use `break` and `continue` for control flow.
**Mojo using a typed for-loop**:
```mojo
var nums: List[Int] = [0, 1, 2, 3, 4]
var squares2: List[Int] = []
for x in nums:
if x % 2 == 0:
squares2.append(x * x)
print(squares2) # [0, 4, 16]
```
**Mojo using a while loop**:
```mojo
var squares3: List[Int] = []
var idx = 0
while idx < 3:
squares3.append(idx * idx)
idx += 1
print(squares3) # [0, 1, 4]
```
## Function definitions
In Python, you can write a function without specifying the types of its
arguments or return value. In Mojo, you must declare types explicitly.
**Python**:
```python
def add(a, b):
return a + b
```
**Mojo**:
```mojo
def add(a: Int, b: Int) -> Int:
return a + b
```
The optional `->` syntax declares the return type.
## Error handling
Error handling in Mojo looks very similar to Python. You raise and catch
exceptions.
**Python**:
```python
try:
raise ValueError("bad input")
except ValueError as e:
print(e) # bad input
```
The `raises` keyword in function and method declarations indicates that
a function may generate or propagate errors.
**Mojo**:
```mojo
try:
raise Error("bad input")
except e:
print(e) # bad input
```
You can specify error types by adding a type name after the `raises`
keyword. This lets you catch the error and use the type instance
directly in your `except` clause:
```mojo
@fieldwise_init
struct MyCustomError(Writable):
var message: String
def test_typed_error() raises MyCustomError: # Typed error
raise MyCustomError("custom error occurred")
try:
test_typed_error()
except e:
print(e.message) # custom error occurred
```
Functions that don't handle the errors they raise automatically
delegate error handling to their caller. You must declare these
functions with the `raises` keyword:
```mojo
def another_raising_function() raises:
raise Error("Message") # Error raised here
def raising_function() raises:
another_raising_function() # Error continues to pass
def handles_errors():
try:
raising_function() # Error handled in this non-raising function
except e:
# handle error here
```
Note that in Mojo, each `try/except` statement can handle a single error type.
## Types: classes vs structs
Python classes are flexible and dynamic. You can add attributes at
runtime, mix types, and override behavior freely. Mojo uses `struct`, a
statically typed alternative that the compiler can optimize
aggressively.
Mojo structs are stack-allocated. The value lives in a fast, fixed-size
region of memory rather than on the heap, where a garbage collector
must track and clean it up.
**Python**:
```python
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
```
**Mojo**:
Mojo initializers require `out self`. The `out` keyword indicates
that the method returns a value through an argument. In initializers,
that argument is `self`, and the instance's fields are guaranteed
to be fully initialized:
```mojo
struct Point:
var x: Int
var y: Int
def __init__(out self, x: Int, y: Int):
self.x = x
self.y = y
...
def main():
var point = Point(5, 3)
print(point.x, point.y) # 5, 3
```
Structs deliver performance and predictability.
## Types: variable static typing
**Python**:
In Python, you may assign different types to the same variable.
```python
a = "x" # String
a = 10 # Not an error
```
**Mojo**:
In Mojo, once a name is bound in a scope, its type is fixed and
can't be rebound to a different type:
```mojo
var a = 1 # Int
a = "string" # Error: can't implicitly convert String to Int
```
When you use `Variant`, you can switch between the types it enumerates.
The variable itself remains statically typed as a `Variant`, even though
the concrete value it holds may change:
```mojo
from std.utils import Variant
from std.testing import *
comptime StringOrInt = Variant[String, Int]
var a: StringOrInt = 1 # Initial value, 1
assert_true(a.unsafe_get[Int]() == 1)
a = "string" # Not an error, "string"
assert_true(a.unsafe_get[String]() == "string")
```
## Types and polymorphism: duck typing vs. traits
In Python, duck typing means you don't declare what interface an object
must have. If it has the method you call, it works at runtime. This is
flexible, but it gives you no safety net.
Mojo uses traits to solve the same problem explicitly. A trait defines
the methods a type must implement or provides a default implementation.
The compiler verifies that any type used in that role actually provides
those methods, so mistakes surface before your code runs.
**Python duck typing**:
```python
def sketch(shape):
shape.draw() # Works if shape has draw(), fails at runtime if not
```
**Mojo traits**:
```mojo
trait Drawable:
def draw(self):
... # required method
def sketch[T: Drawable](shape: T):
shape.draw() # Compiler guarantees shape has `draw()`
```
## Memory management
In Python, memory access is indirect, which adds overhead. Mojo uses
direct memory access, speeding up execution.
Python manages memory automatically using reference counting and a
garbage collector. Reference counting deallocates objects when their
count reaches zero. The garbage collector runs in the background to
clean up objects that are no longer reachable. This removes the need
to manage memory manually, but it also means you have no control over
when collection happens.
Mojo uses ownership semantics with ASAP ("as soon as possible")
destruction. The compiler knows exactly when a value is used for the
last time and its lifetime ends. Memory is freed at that point, without
waiting for a garbage collector to run.
If you need manual memory control, Mojo offers a suite of pointer and
allocation options. You get convenience by default, and precise
`alloc()` and `dealloc()` control when you reach for it.
## Python instincts that may surprise you in Mojo
**"I don't need types."**:
In Python, that's often fine. In Mojo, types are how the compiler
generates fast, optimized code. Untyped code works in dynamic languages
like Python, but it can leave performance, correctness, readability, and
maintainability on the table.
**"Everything is mutable."**:
In Mojo, variables are mutable by default, but function and method
arguments may not be.
**"I can mix types in a list."**:
Mojo collections use static types and benefit from the performance that
brings.
**"Threads are how I parallelize."**:
Python threads are limited by the GIL. Mojo supports parallelism at
multiple levels, from data-parallel SIMD operations to multi-threaded
GPU execution. This isn't limited to threads. Mojo enables low-level
SIMD parallelism and higher-level parallelism across GPUs.
**"Classes are the natural way to structure things."**:
Mojo's `struct` value type and its traits offer a better fit for
performance-sensitive code.
## Use Mojo with AI coding assistants
If you're using an AI coding assistant to help translate Python code
to Mojo, install Mojo agent skills. The `mojo-python-interop` skill
handles the patterns that trip models up like `PythonObject` wrapping,
`import` conventions, and type conversions between the two languages.
```bash
npx skills add modular/skills
```
This installs all four [Mojo agent skills](/docs/tools/skills),
including `mojo-syntax` for general language accuracy.
## The Mojo mindset
Mojo gives you Pythonic ergonomics with systems-level control. That
control comes when you embrace types, ownership, and value semantics.
You don't have to use all of it at once, but it's there for when you
need it.
---
## Mojo quickstart
Welcome to Mojo, a systems language for the AI era.
This tutorial offers a quick tour of Mojo language fundamentals by showing
you how to build a simple application. You'll get a taste of Mojo's syntax
and enough familiarity to read and write basic Mojo code.
It should take about 15-30 minutes if you stop to explore, or less if
you work straight through.
## Setup
Before starting:
- Check the [system requirements](/docs/requirements/).
- [Install Mojo](/install/) in a `pixi` or `uv` environment.
- Open a terminal and make sure `mojo` is in your path or environment.
As you work through this tour, look for *Takeaways* items. They connect
new Mojo syntax to concepts you may already know from other languages.
## Hello Mojo
Create `analyzer.mojo` in your favorite IDE or editor.
Add this to `analyzer.mojo`:
```mojo
def main():
print("Temperature Analyzer")
```
Run it:
```sh
mojo analyzer.mojo
```
### Takeaways
- If you see "Temperature Analyzer", your setup works.
- All Mojo executables use `main()` as their entry point.
## Variables and data
Update your file to add temperature data:
```mojo
def main():
print("Temperature Analyzer")
# [Float64] sets the List element type at compile time
var temps: List[Float64] = [20.5, 22.3, 19.8, 25.1]
print("Recorded", len(temps), "temperatures")
```
## Loops
Print each temperature. Add to the end of `main()`:
```mojo
def main():
# ... existing code ...
for index in range(len(temps)): # The range is [0, len(temps))
print(t" Day {index + 1}: {temps[index]}°C")
```
### Takeaways
- This loop uses indexes. Normally you iterate over elements.
- The `t"..."` prefix creates a *template string*. Braces `{}` interpolate
expressions into the output. This avoids memory allocations for
intermediate values.
:::tip Worth knowing
Mojo also has a `while` loop.
:::
## Functions
Add a function above `main()` to calculate the average temperature:
```mojo
def calculate_average(temps: List[Float64]) -> Float64:
# A literal with a decimal component defaults to Float64
var total = 0.0
for temp in temps:
total += temp
return total / Float64(len(temps))
def main():
# ... existing code ...
```
Call the function by adding this to the end of `main()`:
```mojo
var avg = calculate_average(temps)
print(t"Average: {round(avg, 2)}°C")
```
### Takeaways
- Mojo's `def` functions don't raise by default.
- `round()` returns `avg` rounded to two decimal places.
## Conditionals
Classify the average temperature. Add to the end of `main()`:
```mojo
if avg > 25.0:
print("Status: Hot week")
elif avg > 20.0:
print("Status: Comfortable week")
else:
print("Status: Cool week")
```
## Raise errors
Empty data means no average. Update `calculate_average()` to handle the
error:
- Add `raises` before the return arrow.
- Add an empty list check.
- Raise an `Error` if it's empty.
```mojo
def calculate_average(temps: List[Float64]) raises -> Float64:
if len(temps) == 0:
raise Error("No temperature data")
var total = 0.0
for temp in temps:
total += temp
return total / Float64(len(temps))
```
After updating, your app will no longer compile.
Once `calculate_average()` can raise, its callers must handle or propagate
the error. You do that in the next step.
## Handle errors
Wrap failable code in `try-except` for error handling:
```mojo
try:
var avg = calculate_average(temps)
print(t"Average: {round(avg, 2)}°C")
if avg > 25.0:
print("Status: Hot week")
elif avg > 20.0:
print("Status: Comfortable week")
else:
print("Status: Cool week")
except e:
print("Error:", e)
```
To test the error, replace `temps` with `[]`.
Confirm that your app errors with "No temperature data".
### Takeaways
- Each `try` statement requires at least one `except` or `finally` clause.
- An `else` clause runs only if no error occurs.
- A `finally` clause always runs.
```mojo
try:
operation()
except e:
handle_error(e) # Runs if an error occurs
else:
on_success() # Runs only if no error occurred
finally:
cleanup() # Always runs
```
## Python integration
Mojo integrates with libraries written in other languages. Use Python's NumPy
to calculate the standard deviation.
Install NumPy with your package manager:
```bash
pixi add numpy
```
or:
```bash
uv pip install numpy
```
Add these imports at the top of your file:
```mojo
from std.python import Python
from std.python.numpy import copy_to_numpy_array
```
Then calculate the standard deviation at the end of the `try` block in
`main()`:
```mojo
var np = Python.import_module("numpy")
var pytemps = copy_to_numpy_array(temps)
var std_dev = np.std(pytemps)
print("Temperature standard deviation:", std_dev)
```
## Final code
Your complete `analyzer.mojo`:
```mojo
from std.python import Python
from std.python.numpy import copy_to_numpy_array
def calculate_average(temps: List[Float64]) raises -> Float64:
if len(temps) == 0:
raise Error("No temperature data")
var total = 0.0
for temp in temps:
total += temp
return total / Float64(len(temps))
def main():
print("Temperature Analyzer")
var temps: List[Float64] = [20.5, 22.3, 19.8, 25.1]
print("Recorded", len(temps), "temperatures")
for index in range(len(temps)):
print(t" Day {index + 1}: {temps[index]}°C")
try:
var avg = calculate_average(temps)
print(t"Average: {round(avg, 2)}°C")
if avg > 25.0:
print("Status: Hot week")
elif avg > 20.0:
print("Status: Comfortable week")
else:
print("Status: Cool week")
var np = Python.import_module("numpy")
var pytemps = copy_to_numpy_array(temps)
var std_dev = np.std(pytemps)
print("Temperature standard deviation:", std_dev)
except e:
print("Error:", e)
```
## What you touched
You just used: Mojo variables, lists, loops, functions, conditionals, error
handling, and Python integration in one working program.
## Your first day? Try these
- Build Conway's Game of Life with the [get started
tutorial](/docs/manual/get-started). It normally takes about 45-60
minutes.
- Keep the [language reference](/docs/reference/) handy for syntax,
keywords, and more.
- Download the [cheat sheets](/docs/reference/cheat-sheets/) for printable
reference cards that bring entire concepts together.
- Play [Mojo Quest](https://quest.mojolang.org/) to practice Mojo syntax
with coding challenges.
## Use Mojo with AI coding assistants
If you use an AI coding assistant, install the Mojo agent skills to give it
up-to-date information about the rapidly evolving language:
```sh
npx skills add modular/skills
```
This installs all [Mojo agent skills](/docs/tools/skills), including the
`mojo-syntax` skill for the latest nightly language releases.
## Keep going
- Learn Mojo in depth with the [Mojo Manual](/docs/manual).
- Look up APIs in the [Standard Library](/docs/std).
- Ask questions and connect with other Mojo developers on the
[Discourse forums](https://forum.modular.com/docs/community) and
[Discord](https://discord.com/invite/modular).
- Follow Mojo development on the [Mojo Blog](https://www.modular.com/blog).
---
## Mojo structs
A struct is Mojo's primary way to define your own type. When you want to model
both data and behavior—whether that's a small value type, a numeric
abstraction, or the foundation of a larger system—use a struct.
At a high level, a Mojo struct lets you bundle data together with the
operations that act on that data. This makes structs a natural way to represent
concepts in your program, rather than passing loosely related values through
functions.
Each Mojo `struct` is a data structure that lets you encapsulate _fields_ and
_methods_ to store and operate on data. Structs can define the following
members:
- **Fields** are variables that store data relevant to the struct.
- **Methods** are functions defined in a struct that normally act upon the field
data.
- **Static methods** are functions provided by the type to perform behaviors,
provide constants, or create specialized instances.
- **Dunder methods** are named for their _d_ouble _under_-scored form, with
`__` on both sides. Also called "special methods," [they help define
behaviors](/docs/manual/structs/#special-methods) such as initialization
and allow structs to conform to [traits](/docs/manual/traits/).
- **`comptime` members** enable compile-time references that can be used for
optimization.
For example, if you're building a graphics program, you can use a struct to
define an `Image` that has fields to store information about each image (such
as its component pixels) and methods that perform actions on it (such as
rotating the image).
Mojo's struct format is designed to provide a static, memory-safe data
structure that's both powerful and performant. Unlike dynamic objects
(such as Python classes) that can be modified freely at runtime,
structs are defined at compile time, which allows Mojo to generate
highly optimized code.
All struct fields must be declared using `var` and include a type
annotation. This requirement is part of Mojo's compile-time guarantees,
helping ensure both performance and memory safety.
## Struct definition
You can define a simple struct called `MyPair` with two fields like this:
```mojo
struct MyPair:
var first: Int
var second: Int
```
However, you can't instantiate this struct because it has no initializer
method. So here it is with an initializer to initialize the two fields:
```mojo
struct MyPair:
var first: Int
var second: Int
def __init__(out self, first: Int, second: Int):
self.first = first
self.second = second
```
Notice that the first argument in the `__init__()` method is `out self`.
You'll have a `self` argument as the first argument on all struct methods.
It references the current struct instance (it allows code in the method to
refer to "itself"). _When you call the initializer, you never pass a value
for `self`—Mojo passes it in automatically._
The `out` portion of `out self` is an [argument
convention](/docs/manual/values/ownership#argument-conventions) that declares
`self` as a mutable reference that starts out as uninitialized and must be
initialized before the function returns.
Many types use a field-wise initializer like the one shown for `MyPair` above:
it takes an argument for each field, and initializes the fields directly from
the arguments. To save typing, Mojo provides a
[`@fieldwise_init`](/docs/reference/decorators/fieldwise-init/) decorator, which
generates a field-wise initializer for the struct. So you can rewrite the
`MyPair` example above like this:
```mojo
@fieldwise_init
struct MyPair:
var first: Int
var second: Int
```
The `__init__()` method is one of many [special methods](#special-methods)
(also known as "dunder methods" because they have *d*ouble *under*scores) with
pre-determined names.
:::note
You can't assign values when you declare fields. You must initialize
all of the struct's fields in the initializer. (If you try to leave a field
uninitialized, the code won't compile.)
:::
## Constructing a struct type
Once you have an initializer, with `__init__()` or using `@fieldwise_init`,
you can create an instance of `MyPair` and set the fields:
```mojo title="Construct an instance"
var mine = MyPair(2, 4)
print(mine.first)
```
```output
2
```
:::note Initializer lists
Mojo initializer lists let you construct instances without spelling
out the full type name and parameters. If the full type can be inferred
from context, pass the initializer arguments directly between braces,
with or without keywords. For example `{0.5, fish="salmon"}` calls
`__init__(0.5, fish="salmon")` on the appropriate struct type. This
is equivalent to `MyStruct(0.5, fish="salmon")` if the type is inferred
to be `MyStruct`.
:::
## Making a struct Copyable {#making-a-struct-copyable-and-movable}
By default, Mojo structs can be _moved_, but not _copied_.
For example, the following code produces errors:
```mojo
var a = MyPair(1, 2)
# Implicit copy
var b = a # value of type 'MyPair' cannot be implicitly copied,
# it does not conform to 'ImplicitlyCopyable'
# Explicit copy
var c = a.copy() # 'MyPair' has no attribute 'copy'
# it does not conform to 'Copyable'
# Move
var d = a^ # OK
```
In most cases, you can make a struct copyable just by adding the
`Copyable` [trait](/docs/manual/traits/).
### Copyability
To make a struct copyable, add the `Copyable` trait:
```mojo
struct MyPair(Copyable):
...
```
In most cases, that's all you need to do. Mojo generates a copy
initializer (`__init__(out self, *, copy: Self)` method) for you. You don't
need to write your own unless you need custom logic in the copy
initializer; for example, if your struct dynamically allocates memory. For
more information, see the section on [copy
initializers](/docs/manual/lifecycle/life/#copy-constructor).
The [`Copyable`](/docs/std/traits/copyable/Copyable/) trait provides two ways
to copy a value: the `copy()`
instance method and the copy initializer. Prefer the `copy()` method.
### Implicit copyability
To make a struct implicitly copyable, add the
[`ImplicitlyCopyable`](/docs/std/traits/copyable/ImplicitlyCopyable/) trait:
```mojo
struct MyPair(ImplicitlyCopyable):
...
```
`ImplicitlyCopyable` automatically implies `Copyable` and `Movable`, so all
the notes related to copyability apply here. A type should only be implicitly
copyable if copying the type is inexpensive and has no side effects. Unnecessary
copies can be a big drain on memory and performance, so use this trait with
caution.
## Fields
Fields store a struct's data. When you declare a field, it becomes part of the
struct's memory layout. Because the compiler knows every field's type at
compile time, it can:
- Calculate the struct's exact memory footprint
- Ensure all fields are initialized before use
- Generate fast, direct access to field data
- Prevent changes to the struct's layout at runtime
Fields share the lifetime of their struct instance. They are created when the
struct is created and destroyed when the struct is destroyed. This model avoids
dangling references and partially constructed objects.
Outside of your struct implementation, you access fields with dot notation
(`my_struct.field_name`). Within the struct, your methods access fields using
`self` (`self.field_name`). Mojo knows each field's location at compile time,
making field access direct and efficient.
### Field requirements
**You must** declare field members with `var` in structs:
```mojo
struct MyStruct:
value: Int # Error. Missing `var` keyword
var count: Int # Yes
```
Unlike local variables in functions, this requirement lets Mojo reason about a
struct's layout and guarantees that its memory is safe and predictable.
**You must** use unique symbols for fields, methods, or `comptime` members.
These all exist in the same namespace:
```mojo
struct MyStruct:
var count: Int
var count: String # Error. Invalid redeclaration of `count`
```
**You can** re-use a struct member's name for an argument or method variable.
```mojo
struct MyStruct:
var foo: Int
def use_argument(self, foo: Int): # Argument shadows field
print(foo) # Prints argument value
def use_local(self, value: Int):
var foo = value # Local variable shadows field
print(foo, self.foo) # Prints local, then field
```
**You must** mark `self` as mutable if updating a field value.
```mojo
struct MyStruct:
var foo: Int
def update_foo(mut self, new_value: Int):
self.foo = new_value
```
**You must** initialize fields within initializers, and not
at the point of declaration.
```mojo
struct MyStruct:
var foo: Int = 10 # Error: Unknown tokens
comptime bar = 10 # Yes
```
[`comptime` members](/docs/manual/parameters/#comptime-members)
are compile-time constants (not fields) and don't occupy instance
storage, so they can be initialized at the point of declaration.
### Field conventions
Like other Mojo elements, fields normally adhere to
[certain conventions](https://github.com/modular/modular/blob/mojo/v1.1.0/Mojo/docs/contributing/stdlib/stdlib-code-style.md#code-conventions):
You should use conventional naming for field members:
- Prefer lowercase snake_case for field names (for example, `user_count`,
`max_capacity`).
- Use descriptive names that indicate purpose, and not their type (for example,
`error_msg` not `msg_string`).
- For members meant for internal use or to maintain invariants, add an
underscore prefix (for example, `_private_field`).
- For boolean fields, use `is_` or `has_` prefixes (for example, `is_valid`,
`has_data`).
- Avoid single-letter names except for common mathematical conventions (such as
`x`, `y`, `z` for coordinates).
## Methods
In addition to special methods like `__init__()`, you can add any other method
you want to your struct. For example:
```mojo
@fieldwise_init
struct MyPair:
var first: Int
var second: Int
def get_sum(self) -> Int:
return self.first + self.second
```
```mojo
var mine = MyPair(6, 8)
print(mine.get_sum())
```
```output
14
```
Notice that `get_sum()` also uses the `self` argument, because this is
the only way you can access the struct's fields in a method. The name `self` is
just a convention, and you can use any name you want to refer to the struct
instance that is always passed as the first argument.
Methods that take the implicit `self` argument are called _instance methods_
because they act on an instance of the struct.
:::note
The `self` argument in a struct method is the only argument in a `def`
function that doesn't require a type. You can include the type if you
want, but you can elide it because Mojo already knows its type
(`MyPair` in this case).
:::
## Mutating methods {#mutating-a-struct}
By default, a struct's methods receive an immutable `self`,
so they can't modify the struct's fields. For example:
```mojo
struct MyStruct:
var value: Int
def increment(self):
self.value += 1 # ERROR: expression must be mutable in assignment
# ...
```
To allow a method to mutate the instance, declare its receiver as `mut self`.
This makes `self` mutable inside the method and allows changes to its fields
that persist after the method returns:
```mojo
struct MyStruct:
var value: Int
def increment(mut self):
self.value += 1 # Works: Mutable `self` allows assignment
#...
```
:::note
Read more about mutable arguments and the `mut` keyword:
[Mutable arguments (`mut`)](/docs/manual/values/ownership/#mutable-arguments-mut)
:::
### Static methods
A struct can have _static methods_. A static method can be called without
creating an instance of the struct. Unlike instance methods, a static method
doesn't receive the implicit `self` argument, so it can't access any fields on
the struct.
To declare a static method, use the
[`@staticmethod`](/docs/reference/decorators/staticmethod/) decorator and don't
include
a `self` argument:
```mojo
struct Logger:
def __init__(out self):
pass
@staticmethod
def log_info(message: String):
print("Info: ", message)
```
You can invoke a static method by calling it on the type (in this case,
`Logger`). You can also call it on an instance of the type. Both forms are
shown below:
```mojo
Logger.log_info("Static method called.")
var l = Logger()
l.log_info("Static method called from instance.")
```
```output
Info: Static method called.
Info: Static method called from instance.
```
## Structs compared to classes
If you're familiar with other object-oriented languages, then structs might
sound a lot like classes, and there are some similarities, but also some
important differences. Eventually, Mojo will also support classes to match the
behavior of Python classes.
So, let's compare Mojo structs to Python classes. They both support methods,
fields, operator overloading, decorators for metaprogramming, and more, but
their key differences are as follows:
- Python classes are dynamic: they allow for dynamic dispatch, monkey-patching
(or "swizzling"), and dynamically binding instance fields at runtime.
- Mojo structs are static: they are bound at compile-time (you cannot add
methods at runtime). Structs allow you to trade flexibility for performance
while being safe and easy to use.
- Mojo structs don't support inheritance ("sub-classing"), but a struct can
implement [traits](/docs/manual/traits/).
- Python classes support class attributes—values that are shared by all
instances of the class, equivalent to class variables or static data members
in other languages.
- Mojo structs don't support static data members.
Syntactically, the biggest difference compared to a Python class is that all
fields in a struct must be explicitly declared with `var`.
In Mojo, the structure and contents of a struct are set at compile time and
can't be changed while the program is running. Unlike in Python, where you can
add, remove, or change attributes of an object on the fly, Mojo doesn't allow
that for structs.
However, the static nature of structs helps Mojo run your code faster. The
program knows exactly where to find the struct's information and how to use it
without any extra steps or delays at runtime.
Mojo's structs also work really well with features you might already know from
Python, like operator overloading (which lets you change how math symbols like
`+` and `-` work with your own data, using [special
methods](#special-methods)).
As mentioned above, Mojo builds all its standard types
([`Int`](/docs/std/simd/#int),
[`String`](/docs/std/collections/string/string/String/), etc.) from structs,
rather than hardwiring them into the language itself. This gives you more
flexibility and control when writing your code, and it means you can define your
own types with all the same capabilities (there's no special treatment for the
standard library types).
## Special methods
Special methods (or "dunder methods") such as `__init__()` are pre-determined
method names that you can define in a struct to perform a special task.
Although it's possible to call special methods with their method names, the
point is that you never should, because Mojo automatically invokes them in
circumstances where they're needed (which is why they're also called "magic
methods"). For example, Mojo calls the `__init__()` method when you create
an instance of the struct; and when Mojo destroys the instance, it calls the
`__deinit__()` method (if it exists).
Even operator behaviors that appear built-in (`+`, `<`, `==`, `|`, and so on)
are implemented as special methods that Mojo implicitly calls upon to perform
operations or comparisons on the type that the operator is applied to.
Mojo supports a long list of special methods; far too many to discuss here, but
they generally match all of [Python's special
methods](https://docs.python.org/3/reference/datamodel#special-method-names)
and they usually accomplish one of two types of tasks:
- Operator overloading: A lot of special methods are designed to overload
operators such as `<` (less-than), `+` (add), and `|` (or) so they work
appropriately with each type. For more information, see
[Implement operators for custom
types](/docs/manual/structs/operator-support/).
- Lifecycle event handling: These special methods deal with the lifecycle
and value ownership of an instance. For example, `__init__()` and
`__deinit__()` demarcate the beginning and end of an instance lifetime,
and other special methods define the behavior for other lifecycle events
such as how to copy or move a value.
You can learn all about the lifecycle special methods in the [Value
lifecycle](/docs/manual/lifecycle/) section. However, most structs are simple
aggregations of other types, so unless your type requires custom behaviors when
an instance is created, copied, moved, or destroyed, you can synthesize the
essential lifecycle methods you need (and save yourself some time) using the
`@fieldwise_init` decorator (described in
[Struct definition](#struct-definition)), and the `Copyable` and `Movable`
traits (described in
[Making a struct copyable](#making-a-struct-copyable-and-movable)).
---
## Add operator support to custom types
Each Mojo operator maps to a set of dunder methods you can add to your
struct implementation. These methods let you use operator syntax instead of
calling methods directly.
Knowing your operators and their related methods opens the full suite of
operator syntax to your custom structs.
## Forward, reverse, and in-place methods
Each binary operator uses up to three method forms. For example,
consider the addition `a + b`:
- **Forward**: Mojo tries `a.__add__(b)` first.
- **Reverse**: If the forward method doesn't exist or can't handle `b`'s
type, Mojo falls back to `b.__radd__(a)`.
- **In-place**: For `a += b`, Mojo calls `a.__iadd__(b)`.
Reversed methods exist for mixed-type expressions where the
left operand doesn't know about the right operand's type:
```mojo
a + 5 # calls a.__add__(5)
5 + a # Int doesn't know your type, falls back
# to a.__radd__(5)
a += 5 # calls a.__iadd__(5)
```
## Unary operators
A unary operator returns the original value if unchanged, or a new value
representing the result. For example, `-x` uses the unary negation operator:
```mojo
@fieldwise_init
struct MyInt:
var value: Int
def __neg__(self) -> Self:
return Self(-self.value)
```
If `x` is a `MyInt`, then `-x` returns a new instance with its `value`
field negated.
## Comparison operators and traits
Operators do not require that you conform your types to traits. However,
there are benefits to doing so.
The `Comparable` trait provides defaults for `<=`, `>`, and `>=`. You just
implement `__lt__()` and `__eq__()`.
Similarly, the `Equatable` trait provides defaults for `__eq__()` and
`__ne__()` when all fields are `Equatable`.
For types without a natural ordering (like complex numbers), only implement
`Equatable`, and not `Comparable`.
## Subscript operators
Implement `__getitem__()` for reads and `__setitem__()` for writes. Both
subscripting methods accept variadic arguments for multi-dimensional
indexing.
For a simple one-dimensional collection, you unlock subscripting with a
simple index:
```mojo
struct MySeq[T: Copyable]:
def __getitem__(self, idx: Int) -> T:
...
def __setitem__(mut self, idx: Int, value: T):
...
```
For multi-dimensional collections, make use of variadics or multiple index
arguments:
```mojo
struct Grid[T: Copyable]:
# Fixed two dimensions
def __getitem__(self, x: Int, y: Int) -> T:
...
# Arbitrary dimensions
def __getitem__(self, *indices: Int) -> T:
...
```
Custom subscripts can support slicing as well as indices, such as
`obj[1:5]`. Implement `__getitem__()` with a
[`Slice`](/docs/std/builtin/builtin_slice/Slice/) parameter instead of
`Int`.
Each `Slice` has three optional fields: `start`, `end`, and `step`. You
normalize these by calling `indices()`. Pass your type's size. This returns
a triplet of values representing the span adjusted to your extent,
resolving omitted values or negative indices into non-negative positions:
```mojo
struct MySeq[T: Copyable]:
var size: Int
def __getitem__(self, span: Slice) -> Self:
var start: Int
var end: Int
var step: Int
start, end, step = span.indices(self.size)
...
```
## Walkthrough: Build a `Complex` type
The next sections incrementally build a `Complex` struct.
This example demonstrates every category of operator implementation.
In this walk-through, you'll work with unary operators, binary operators
with same-type and mixed-type operands, reversed methods, in-place
assignment, equality comparison, Boolean conversion, and subscript access.
:::note
The standard library includes
[`ComplexSIMD`](/docs/std/complex/complex/ComplexSIMD/),
a parameterized complex number type with basic arithmetic
support. The `Complex` type in this example is independent
and not based on `ComplexSIMD`.
:::
## Create the base type
A complex number holds real and imaginary parts, stored in the
real `re` and imaginary `im` fields:
```mojo
from std.math import sqrt
@fieldwise_init
struct Complex(
Boolable,
Equatable,
TrivialRegisterPassable,
Writable,
):
var re: Float64
var im: Float64
```
- Conforming to `TrivialRegisterPassable` gives you value semantics
without needing to write special lifecycle methods.
- `Equatable` lets you compare two instances, and `Writable` produces
output for `print()` statements.
- `Boolable` lets you use a `Complex` value in a Boolean context, such as
an `if` condition.
### Convenience initializer
Adding a convenience initializer lets you create instances using
only the real part of your number:
```mojo
def __init__(out self, re: Float64):
self.re = re
self.im = 0.0
```
## Make your type printable
Implementing `Writable` lets you use `print()` and `String()` directly.
This custom implementation provides parentheses and separate real and
imaginary output.
```mojo
# Struct method
def write_to(self, mut writer: Some[Writer]):
writer.write("(", self.re)
if self.im < 0:
writer.write(" - ", -self.im)
else:
writer.write(" + ", self.im)
writer.write("i)")
```
You can also implement `write_repr_to()` to define the value's
*representation*—the developer-facing form returned by `repr()`. This
implementation produces a string that mirrors how you'd construct the value
in code:
```mojo
# Struct method
def write_repr_to(self, mut writer: Some[Writer]):
t"Complex(re = {self.re}, im = {self.im})".write_to(writer)
```
```mojo
var c = Complex(3.14, -2.72)
print(c) # (3.14 - 2.72i)
print(repr(c)) # Complex(re = 3.14, im = -2.72)
```
## Add unary operator support
`+c` returns the value unchanged. `-c` negates both
components:
```mojo
# methods
def __pos__(self) -> Self:
return self
def __neg__(self) -> Self:
return Self(-self.re, -self.im)
...
var c = Complex(-1.2, 6.5)
print(+c) # (-1.2 + 6.5i)
print(-c) # (1.2 - 6.5i)
```
## Support binary arithmetic
Add addition, subtraction, multiplication, and division between
two `Complex` values with dunders. Each form returns a new `Complex`
instance:
```mojo
def __add__(self, rhs: Self) -> Self:
return Self(self.re + rhs.re, self.im + rhs.im)
def __sub__(self, rhs: Self) -> Self:
return Self(self.re - rhs.re, self.im - rhs.im)
def __mul__(self, rhs: Self) -> Self:
return Self(
self.re * rhs.re - self.im * rhs.im,
self.re * rhs.im + self.im * rhs.re,
)
def __truediv__(self, rhs: Self) -> Self:
var denom = rhs.squared_norm()
return Self(
(self.re * rhs.re + self.im * rhs.im) / denom,
(self.im * rhs.re - self.re * rhs.im) / denom,
)
def squared_norm(self) -> Float64:
return self.re * self.re + self.im * self.im
def norm(self) -> Float64:
return sqrt(self.squared_norm())
```
```mojo
var c1 = Complex(-1.2, 6.5)
var c2 = Complex(3.14, -2.72)
print(c1 + c2) # (1.94 + 3.78i)
print(c1 * c2) # (13.91 + 23.67i)
```
## Add mixed-type arithmetic with reversed methods
To support expressions like `2.5 + c` where `Float64` is on
the left, you need both overloaded forward methods and
reversed methods. Without `__radd__()`, `2.5 + c` would fail
because `Float64` doesn't know about `Complex`:
```mojo
# Forward: Complex + Float64
def __add__(self, rhs: Float64) -> Self:
return Self(self.re + rhs, self.im)
# Reversed: Float64 + Complex
def __radd__(self, lhs: Float64) -> Self:
return Self(self.re + lhs, self.im)
def __sub__(self, rhs: Float64) -> Self:
return Self(self.re - rhs, self.im)
def __rsub__(self, lhs: Float64) -> Self:
return Self(lhs - self.re, -self.im)
def __mul__(self, rhs: Float64) -> Self:
return Self(self.re * rhs, self.im * rhs)
def __rmul__(self, lhs: Float64) -> Self:
return Self(lhs * self.re, lhs * self.im)
def __truediv__(self, rhs: Float64) -> Self:
return Self(self.re / rhs, self.im / rhs)
def __rtruediv__(self, lhs: Float64) -> Self:
var denom = self.squared_norm()
return Self(
(lhs * self.re) / denom,
(-lhs * self.im) / denom,
)
```
Now both orderings work:
```mojo
var c = Complex(-1.2, 6.5)
print(c + 2.5) # (1.3 + 6.5i)
print(2.5 + c) # (1.3 + 6.5i)
print(2.5 * c) # (-3.0 + 16.25i)
```
### Allow in-place assignment
In-place methods modify `self` directly instead of returning
a new value. You can overload for both `Complex` and
`Float64` operands:
```mojo
def __iadd__(mut self, rhs: Self):
self.re += rhs.re
self.im += rhs.im
def __iadd__(mut self, rhs: Float64):
self.re += rhs
def __isub__(mut self, rhs: Self):
self.re -= rhs.re
self.im -= rhs.im
def __isub__(mut self, rhs: Float64):
self.re -= rhs
def __imul__(mut self, rhs: Self):
var new_re = self.re * rhs.re - self.im * rhs.im
var new_im = self.re * rhs.im + self.im * rhs.re
self.re = new_re
self.im = new_im
def __imul__(mut self, rhs: Float64):
self.re *= rhs
self.im *= rhs
def __itruediv__(mut self, rhs: Self):
var denom = rhs.squared_norm()
var new_re = (self.re * rhs.re + self.im * rhs.im) / denom
var new_im = (self.im * rhs.re - self.re * rhs.im) / denom
self.re = new_re
self.im = new_im
def __itruediv__(mut self, rhs: Float64):
self.re /= rhs
self.im /= rhs
...
var c = Complex(-1.0, -1.0)
c += Complex(0.5, -0.5)
print(c) # (-0.5 - 1.5i)
c += 2.75
print(c) # (2.25 - 1.5i)
c *= 0.75
print(c) # (1.6875 - 1.125i)
c /= 2.0
print(c) # (0.84375 - 0.5625i)
```
## Support type equality checks
Complex numbers have no natural ordering, so `Complex` conforms to
`Equatable` (not `Comparable`). This gives you `==` and `!=` without implying
that one complex number is "less than" another.
You don't need to implement `__eq__()` or `__ne__()` yourself—implement them
only when a type needs equality semantics that differ from a memberwise field
comparison. `Equatable` supplies a default `__eq__()` that uses compile-time
reflection to compare every field, and a default `__ne__()` that returns the
inverse of `__eq__()`. A `Complex` is equal to another exactly when both fields
match, so the reflection-based default is exactly the behavior you want. (Bear
in mind that, because a floating-point `NaN` never equals itself, a `Complex`
holding a `NaN` won't equal itself either.)
```mojo
var c1 = Complex(-1.2, 6.5)
var c2 = Complex(-1.2, 6.5)
var c3 = Complex(3.14, -2.72)
print(c1 == c2) # True
print(c1 != c3) # True
```
## Support use in Boolean contexts
Conforming to `Boolable` and implementing `__bool__()` lets you use a
`Complex` value directly in a Boolean context, such as an `if` condition or a
call to `Bool()`. Mojo treats a built-in numeric value as "true" when it's
nonzero, so a natural definition treats a complex number as "true" when either
component is nonzero:
```mojo
def __bool__(self) -> Bool:
return self.re != 0.0 or self.im != 0.0
```
```mojo
var c1 = Complex(0.0, 0.0)
var c2 = Complex(-1.2, 6.5)
print(Bool(c1)) # False
print(Bool(c2)) # True
if c2:
print("c2 is nonzero") # c2 is nonzero
```
## Unlock subscript access
The get and set item dunders allow you to index content within
your type. For this example, the real part of the complex number
is index 0, and index 1 returns the imaginary component:
```mojo
def __getitem__(self, idx: Int) raises -> Float64:
if idx == 0:
return self.re
if idx == 1:
return self.im
raise "index out of bounds"
def __setitem__(mut self, idx: Int, value: Float64) raises:
if idx == 0:
self.re = value
elif idx == 1:
self.im = value
else:
raise "index out of bounds"
...
var c = Complex(3.14)
print(c[0], c[1]) # 3.14 0.0
c[1] = 42.0
print(c) # (3.14 + 42.0i)
```
## Every operator, one walkthrough
This example walked you through every Mojo operator from simple arithmetic
to comparisons to subscripting.
Implementing the right dunders and/or conforming to the right traits enables
you to use operator syntax for nearly any custom type.
---
## Self-referential structs
Some data structures don't fit well with value semantics. Lists, trees,
and graphs all need nodes that point to each other. You can't build these by
nesting one value inside another, because the type would keep growing forever.
In Mojo, you build these shapes with pointers, heap allocation, and manual
cleanup. The idea may feel new at first, but the pattern stays simple once you
see it in small steps.
## Avoid direct self-reference
Mojo doesn't let you build a type that stores another instance of itself, even
when nested within an [`Optional`](/docs/std/collections/optional/Optional/):
```mojo
struct Node:
var value: String
var next: Optional[Node] # ERROR: Recursive reference
# ...
```
Each `struct` has a fixed layout. If `Node` held another `Node` directly, the
compiler wouldn't know how much space to reserve. Optional fields don't help,
because the outer value still needs room for the inner one.
Pointers solve this problem. Pointers have a fixed size, and they let values
point at each other without blowing up the type.
## Adding self-referential pointers
The following code shows how to set up a node that can point to its own type.
This sample gives you a node type with a value slot and a single link
to the next node:
```mojo
struct Node[T: ImplicitlyCopyable & Writable & Deinitable](
Movable
):
comptime NodePointer = Pointer[Self, MutUntrackedOrigin]
var value: Optional[Self.T] # The `Node`'s value
var next: Optional[Self.NodePointer] # Pointer to the next `Node`
# Uses an `Optional` value to allow 'empty' Node construction
# that can be moved into newly allocated memory
def __init__(out self, value: Optional[Self.T] = None):
self.value = value
self.next = {}
```
The code defines a type-specific `NodePointer` type alias built on
[`Pointer`](/docs/std/memory/pointer/Pointer/).
[`MutUntrackedOrigin`](/docs/manual/values/lifetimes/)
lets the pointer represent dynamically-allocated memory that the lifetime
checker doesn't track. You need to both allocate and deallocate memory as
needed.
The `next` field is an `Optional[Self.NodePointer]` because a node may or may
not link to another node. `Pointer` is non-nullable, so `Optional` provides the
null state. `Optional[Pointer]` has the same memory layout as a raw pointer, so
there's no overhead. For more on this pattern, see
[Working with nullability](/docs/manual/pointers/using-pointers/#working-with-nullability).
The optional `value` lets you create "empty" nodes, enabling you to move
new `Node` memory allocations into place.
## Building nodes
Here's the key pattern you can use in many reference structures:
1. Allocate space.
1. Construct a value-holding node.
1. Write it into the allocated memory.
1. Return the pointer.
And here's an example of that pattern:
```mojo
@staticmethod
def make_node(value: Self.T) -> Self.NodePointer:
var node_ptr = alloc[Self]({count = 1}).unsafe_leak()
node_ptr.unsafe_write(Self(value))
return node_ptr
```
In this case, constructing the node (`Self(value)`) is simple enough
that it's inline with the
[`unsafe_write()`](/docs/std/memory/pointer/Pointer/#unsafe_write) call.
This "allocate space, initialize, and write" approach creates safe
pointer-based structures in Mojo.
[`alloc()`](/docs/std/memory/alloc/alloc/) returns an
[`Allocation`](/docs/std/memory/alloc/Allocation/), an owning handle that the
compiler requires you to release before it goes out of scope. That's the right
default, but a node has to outlive the function that allocates it, so
`make_node()` calls
[`unsafe_leak()`](/docs/std/memory/alloc/Allocation/#unsafe_leak) to take the
raw pointer out of the handle. Leaking transfers responsibility for the memory
to you — see
[More memory allocation patterns](/docs/manual/pointers/using-pointers/#more-memory-allocation-patterns)
for when to prefer each approach.
## Freeing nodes
Releasing a node takes two steps:
destroy the value stored in the memory, then release the memory itself.
Because `make_node()` returns a raw pointer, you need to pair the
leaked pointer back up with the layout you allocated it with to get an
`Allocation` that [`dealloc()`](/docs/std/memory/alloc/dealloc/) can consume:
```mojo
@staticmethod
def free_node(var node_ptr: Self.NodePointer):
node_ptr.unsafe_deinit_pointee()
dealloc(
ThinAllocation(unsafe_owned_ptr=node_ptr).unsafe_with_layout(
{count = 1}
)
)
```
The two steps are separate because `dealloc()` releases memory without running
deinitializers on whatever the memory holds. Skipping
[`unsafe_deinit_pointee()`](/docs/std/memory/pointer/Pointer/#unsafe_deinit_pointee)
would leak whatever the node's value owns, which in this case is a
[`String`](/docs/std/collections/string/string/String/)'s heap buffer.
Every place that removes a node calls this one method, so the pairing of
`make_node()` and `free_node()` stays easy to audit.
## Linking nodes
To link nodes, create a new node and set your `next` pointer to point at it.
This example shows how to `append()` a new node using a supplied value.
If a `next` node already exists, the code frees it before appending
the new node.
```mojo
def append(mut self, value: Self.T):
# Free chain if replacing `next`
if self.next:
var next_ptr = self.next.value()
next_ptr[].free_chain()
Self.free_node(next_ptr)
self.next = Self.make_node(value)
```
## Walking the list
To walk the list, follow the chain until you reach the end.
Recursive code makes this easy to read. This example prints
the value stored at each node:
```mojo
@staticmethod
def print_list(node: Optional[Self.NodePointer]):
if not node:
print("Empty list")
return
var node_ptr = node.value()
var current_value: Optional[Self.T] = node_ptr[].value
if current_value:
print(current_value.value(), end=" ")
if node_ptr[].next:
Self.print_list(node_ptr[].next)
else:
print()
```
The pattern is simple: check the value, print it if it exists,
then move to the next link.
:::note
This example uses a static method, but you can also implement
it as an instance method.
:::
## Cleaning up
Because you allocate each node yourself, you're also responsible
for freeing it. This cleanup walks the chain and frees each node
after destroying its pointee:
```mojo
def free_chain(self):
var current = self.next
while current:
var current_ptr = current.value()
var next_node = current_ptr[].next
Self.free_node(current_ptr)
current = next_node
```
Note that the loop reads `current_ptr[].next` *before* freeing the node. Once
`free_node()` returns, the pointer dangles and reading through it would be a
use-after-free.
The "head" node stays allocated unless you explicitly free it yourself:
```mojo
list_head[].free_chain()
ListNode.free_node(list_head)
```
### Deinitializers
When you build real Mojo data structures, you usually want a safe API that
hides raw pointers from users. In a complete linked-list type (rather than
a small demo of linkable nodes) the parent list handles node allocation and
freeing. Because it owns the nodes, it also performs cleanup in its
[deinitializer](/docs/manual/lifecycle/death/).
Here's a small example that shows how to deinitialize `self`:
```mojo
struct LinkedList[T: ImplicitlyCopyable & Writable & Deinitable]:
comptime _Node = Node[T]
var _head: Optional[Self._Node.NodePointer]
def __deinit__(deinit self):
"""Clean up the list by freeing all nodes.
Notes:
Time complexity: O(n) in len(self).
See Also:
"Choose the form of the Destructor!"
-- Gozer, "Ghostbusters" (1984).
"""
var curr = self._head
while curr:
var curr_ptr = curr.value()
var next = curr_ptr[].next
Self._Node.free_node(curr_ptr)
curr = next
```
:::note Putting it together
View full sample code
```mojo
from std.memory import ThinAllocation, dealloc
comptime Element = String # Adapt for your type
comptime ListNode = Node[Element] # Constructing a LinkedList
struct Node[T: ImplicitlyCopyable & Writable & Deinitable](Movable):
comptime NodePointer = Pointer[Self, MutUntrackedOrigin]
var value: Optional[Self.T] # The `Node`'s value
var next: Optional[Self.NodePointer] # Pointer to the next `Node`
# Uses an `Optional` value to allow 'empty' Node construction
# that can be moved into newly allocated memory
def __init__(out self, value: Optional[Self.T] = None):
self.value = value
self.next = {}
# Constructs a `Node` with a `value` with heap allocation and
# returns a pointer to the new `Node`.
@staticmethod
def make_node(value: Self.T) -> Self.NodePointer:
var node_ptr = alloc[Self]({count = 1}).unsafe_leak()
node_ptr.unsafe_write(Self(value))
return node_ptr
# Destroys the pointee, then releases the `Node`'s heap allocation by
# pairing the leaked pointer back up with the layout it was allocated with.
@staticmethod
def free_node(var node_ptr: Self.NodePointer):
node_ptr.unsafe_deinit_pointee()
dealloc(
ThinAllocation(unsafe_owned_ptr=node_ptr).unsafe_with_layout(
{count = 1}
)
)
# Constructs a `Node` with allocated memory, assigns a value, appends
# the pointer to `self.next`. Replaces any existing `next`.
def append(mut self, value: Self.T):
# Free chain if replacing `next`
if self.next:
var next_ptr = self.next.value()
next_ptr[].free_chain()
Self.free_node(next_ptr)
self.next = Self.make_node(value)
# Prints the list starting at this pointer's pointee
@staticmethod
def print_list(node: Optional[Self.NodePointer]):
if not node:
print("Empty list")
return
var node_ptr = node.value()
var current_value: Optional[Self.T] = node_ptr[].value
if current_value:
print(current_value.value(), end=" ")
if node_ptr[].next:
Self.print_list(node_ptr[].next)
else:
print()
# Releases all successively allocated `Node` pointees. Does not release self.
def free_chain(self):
var current = self.next
while current:
var current_ptr = current.value()
var next_node = current_ptr[].next
Self.free_node(current_ptr)
current = next_node
def main():
var values: List[Element] = ["one", "one", "two", "three", "five", "eight"]
var list_head = ListNode.make_node(values[0])
var current = list_head
for idx in range(1, len(values), 1):
current[].append(values[idx])
current = current[].next.value()
ListNode.print_list(list_head)
# Demonstrates cleanup. In short-lived programs, the OS reclaims memory
# at exit
list_head[].free_chain()
ListNode.free_node(list_head)
```
Output:
```output
one one two three five eight
```
:::
## What next?
- Learn more about **pointers and memory safety** in Mojo's
[using pointers](/docs/manual/pointers/using-pointers/)
and [lifetime and origin rules](/docs/manual/values/lifetimes/) guides.
- Learn more about how to **manage cleanup** in the Mojo
[deinitializer](/docs/manual/lifecycle/death/) documentation.
---
## Traits
Traits define contracts between types and the code that use them. Those
contracts describe behavior, such as the methods a type must provide, as
well as related (*associated*) types and constants. When a type conforms
to a trait, the compiler verifies that it satisfies every requirement,
allowing code to *rely* on the trait's interface.
Traits are Mojo's pathway to polymorphism. They let you write code that
works across many types without depending on implementation details that
fall outside the contract. They're especially important for parameterized
types and functions, which let you write code that works across many
concrete types. By constraining a parameterized type to one or more
traits, you give the compiler the information it needs to reason about the
type and verify the code is safe and correct.
Imagine you're writing code that works with many brands of sensors. Each
sensor has its own implementation. They all know how to produce a
reading, a run-time value such as a deflection angle. They also report a
standard error range, a compile-time constant specific to that device.
Each sensor also declares a measurement *type* that determines how the
reading is interpreted. Some sensors measure angles, others distance or
pressure. Your code shouldn't care how a particular sensor works or how it
represents its measurements. An angle measurement, for example, might use
degrees, radians, or gradians. It only needs to know that every sensor
provides the operations and information it depends on.
Traits express those requirements as a single contract. Instead of writing
a function for each sensor type, you write one function against the trait,
and any conforming type can use it. Mojo verifies that every required
method, associated type, and compile-time value is present, so your code
can use them without runtime overhead or capability checks.
Traits are a foundation of Mojo code reuse. You write code in terms of
what a trait requires rather than enumerating concrete types. That keeps
your code flexible while preserving compile-time correctness.
## Defining traits
Traits let the compiler reason about a type's shape and capabilities: its
methods, associated types, and compile-time values. Declare a trait with
the `trait` keyword, followed by a name and a block of requirements:
```mojo
trait DeflectionSensing:
def fetch_reading(self) -> Float64:
...
```
The three dots mark `fetch_reading()` as required. `DeflectionSensing`
doesn't say how a type produces a value, only that a conforming type must
be able to.
### Domain-specific behavior
A trait can provide a default implementation based on the information it
knows about conforming types. The default implementation can call other
required methods, but it can't call methods that aren't part of the
trait's contract.
Default methods are a good fit for domain-specific behavior that can be
expressed in terms of the trait's requirements. For example,
`within_tolerance()` validates a sensor's current reading:
```mojo
trait DeflectionSensing:
def fetch_reading(self) -> Float64:
...
comptime absolute_tolerance: Float64 = 0.05 # This is made up for this example
def within_tolerance(self) -> Bool:
return abs(self.fetch_reading()) <= Self.absolute_tolerance
```
Conforming types inherit default implementations and can override them.
Mojo doesn't provide a way to call a default implementation from an
override.
### Refining other traits
A trait can refine another trait, meaning that it inherits every
requirement from the refined trait while adding new ones. For example,
`CalibratableDeflectionSensing` does everything `DeflectionSensing` does,
but also requires a `calibrate()` method:
```mojo
trait CalibratableDeflectionSensing(DeflectionSensing):
def calibrate(mut self):
...
struct EddyCurrentSensor(CalibratableDeflectionSensing):
def fetch_reading(self) -> Float64:
# its implementation
def calibrate(mut self):
# its implementation
```
A conforming `EddyCurrentSensor` must implement `calibrate()`, while also
meeting every requirement of `DeflectionSensing`. It inherits the default
implementation of `within_tolerance()`, which calls `fetch_reading()` and
uses the `absolute_tolerance` constant.
## The trait contract
A trait contract consists of methods and three kinds of compile-time
members: associated types, required compile-time values, and shared
compile-time constants.
Methods can be required or provided. Required methods must be implemented
by every conforming type. Provided methods include a default
implementation that conforming types can override.
Compile-time members either require each conforming type to provide its
own value or define a value shared by every conforming type.
- *Required methods* use the `...` ellipsis in their body. Every
conforming type must implement them.
```mojo
trait Loggable:
def log(self, message: String):
...
```
- *Provided methods* are implemented in the trait. Conforming types can
override them. Even a default no-op implementation is a valid provided
method.
```mojo
trait Pausable:
def pause(self):
pass
```
- *Associated types* require conforming types to declare a subordinate
type. They're most commonly used in collections, where a parameterized
collection declares an element type.
```mojo
trait Container:
associatedtype Element: Movable
```
- *Required compile-time values* must be defined by every conforming type.
They're often used for values that vary across implementations.
```mojo
trait Pausable:
comptime max_pause_seconds: Float64
```
- *Shared compile-time constants* are defined by the trait and shared by
every conforming type.
```mojo
trait DeflectionSensing:
comptime absolute_tolerance: Float64 = 0.05
```
A trait that declares none of these elements is called a *marker trait*.
It doesn't require any methods, associated types, or compile-time values.
Instead, it marks a conforming type as having a particular property or
capability.
## Conforming to a trait
A struct conforms to a trait by listing it in parentheses after the
struct name and implementing its required methods:
```mojo
@fieldwise_init
struct CapacitiveSensor(Copyable, DeflectionSensing):
def fetch_reading(self) -> Float64:
# Not a very good sensor, but a simple example.
return Float64(21.5)
```
If a struct claims to conform to `DeflectionSensing` but doesn't implement
`fetch_reading()`, it won't compile. At compile time, Mojo verifies that
`CapacitiveSensor` satisfies every `DeflectionSensing` requirement,
including its methods and comptime elements.
Traits don't use duck typing. A struct that implements `fetch_reading()`
but doesn't declare `DeflectionSensing` isn't a conforming type.
### Required comptime members
Define required comptime values directly on the conforming type with
`comptime name = value`. For example, if `Pausable` requires a
`max_pause_seconds` value, you'd declare it like this:
```mojo
@fieldwise_init
struct Timer(Copyable, Pausable):
comptime max_pause_seconds: Float64 = 30.0
def pause(self):
print("Paused")
```
## Parameterizing functions and types with traits
With the `DeflectionSensing` trait, you can build types for specific
sensors, such as `CapacitiveSensor` or `EddyCurrentSensor`.
By conforming to the trait, each type implements all required methods and
comptime members. This shared contract lets you write a single function
that works with any of them:
```mojo
def averaged_poll[
SensorType: DeflectionSensing, // # infer-only
](sensor: SensorType, samples: Int) -> Float64:
var total: Float64 = 0.0
for _ in range(samples):
total += sensor.fetch_reading()
return total / Float64(samples)
```
Since every sensor conforms to `DeflectionSensing`, the compiler knows
that `fetch_reading()` is available:
```mojo
var sensor = CapacitiveSensor()
var average_reading = averaged_poll(sensor, 10)
print("Average reading:", average_reading) # Fixed to 21.5 for the example
```
The call site doesn't use square brackets because the compiler infers
`SensorType` from the argument.
Use the `Some[]` shorthand when you don't need to name the type:
```mojo
def averaged_poll_2(sensor: Some[DeflectionSensing], samples: Int) -> Float64:
var total: Float64 = 0.0
for _ in range(samples):
total += sensor.fetch_reading()
return total / Float64(samples)
```
Use the named form when you need to refer to the type again, for example
to require two arguments of the *same* conforming type:
```mojo
def compare_readings[
SensorType: DeflectionSensing
](a: SensorType, b: SensorType) -> Float64:
return a.fetch_reading() - b.fetch_reading()
```
## Combining traits
A parameter can require more than one trait. Use an ampersand (`&`) to
combine them. Any type passed to the parameter must conform to every
trait in the combination.
For example, you could define a `Loggable` trait and require that a sensor
conform to both `DeflectionSensing` and `Loggable`:
```mojo
trait Loggable:
def log(self, message: String):
...
def poll_and_log[T: DeflectionSensing & Loggable](sensor: T):
print(sensor.fetch_reading())
sensor.log("Polling sensor")
```
Refinement and composition solve different problems. Use refinement when
one trait naturally extends another and that relationship should always
hold. Use composition when a function or type needs multiple independent
capabilities.
### Reusing trait compositions
If you reuse the same combination in multiple places, give it a name with
a `comptime` declaration:
```mojo
comptime SensorLike = DeflectionSensing & Loggable
struct SmartSensor(Copyable, SensorLike):
def fetch_reading(self) -> Float64:
return 18.2
def log(self, message: String):
print("reading logged")
```
`SensorLike` isn't a new trait. It's shorthand for
`DeflectionSensing & Loggable`. Any type that conforms to both traits
automatically satisfies `SensorLike`; there's nothing extra to declare.
## Default implementations
A trait can provide a working implementation instead of just requiring
one:
```mojo
trait DefaultLoggable:
def log(self, message: String):
print("reading logged")
@fieldwise_init
struct BasicSensor(Copyable, DefaultLoggable):
pass
```
`BasicSensor` conforms without implementing `log()`. It inherits the
trait's implementation, but any conforming type can override it by
providing its own `log()`.
Default implementations can conflict. If a type conforms to two traits
that both provide the same method, Mojo won't choose between them:
```mojo
trait PowerCycle:
def restart(self):
print("Restarting via power cycle")
trait Rebootable:
def restart(self):
print("Restarting via soft reboot")
struct Gateway(PowerCycle, Rebootable):
pass
# Error: conflicting default implementations for restart().
```
Resolve the conflict by implementing `restart()` on `Gateway`. Your
implementation overrides both defaults.
## Things to know
**You can't add traits to existing types.** Conformance is declared where
a type is defined. You can't retroactively make `Float64`, `Int`, or any
other type you don't own conform to a new trait.
**Conformance is explicit.** A struct that happens to implement
`fetch_reading()` doesn't conform to `DeflectionSensing` unless it
declares the trait. Mojo checks declared conformance, not just matching
method names.
**Traits are all or nothing.** A conforming type must satisfy every
requirement, either by implementing it directly or by inheriting a
default implementation. There's no partial conformance.
---
## Types
All values in Mojo have an associated data type. Most of the types are
*nominal* types, defined by a [`struct`](/docs/manual/structs/). These types are
nominal (or "named") because type equality is determined by the type's *name*,
not its *structure*.
There are some types that aren't defined as structs:
- Functions are typed based on their signatures.
- `NoneType` is a type with one instance, the `None` object, which is used to
signal "no value."
Mojo comes with a standard library that provides a number of useful types and
utility functions. These standard types aren't privileged. Each of the standard
library types is defined just like user-defined types—even basic types like
[`Int`](/docs/std/simd/#int) and
[`String`](/docs/std/collections/string/string/String/). But these standard
library types are the building blocks you'll use for most Mojo programs.
The most common types are *built-in types*, which are always available and
don't need to be imported. These include types for numeric values, strings,
boolean values, and others.
The standard library also includes many more types that you can import as
needed, including collection types, utilities for interacting with the
filesystem and getting system information, and so on.
## Numeric types
Mojo provides built-in numeric types that represent signed integers, unsigned
integers, and floating-point values. These types support multiple precisions and
are used to model both low-level data and high-level numeric computation.
The following sections introduce integer and floating-point types in Mojo.
:::note
All numeric types support the usual numeric and bitwise operators. The
[`math`](/docs/std/math/) module provides additional math functions.
:::
### Integers and unsigned integers
Mojo's general-purpose integer type is the signed `Int`. For a specific bit
width, or for an unsigned integer, use the fixed-size integer types:
- If you need a fixed-size integer, Mojo provides explicit-width integer types
such as `Int8`, `Int16`, `UInt32`, and `UInt64`.
- Use the general `Int` type when you don't require a specific bit width.
These general and fixed-precision integer types are aliases to the
[`SIMD`](/docs/std/simd/SIMD/) type.
`Int` represents a signed integer that uses the system's native word size,
typically 64 bits on 64-bit CPUs and 32 bits on 32-bit CPUs.
You may wonder when to use `Int` and when to use the other integer
types. In general, `Int` is a good safe default when you need an integer type
and you don't require a specific bit width. Using `Int` as the default integer
type for APIs makes APIs more consistent and predictable.
#### Signed versus unsigned
Signed and unsigned integers with the same bit width can represent the same
number of distinct values, but over different ranges. For example:
- `Int8` represents 256 values ranging from `-128` to `127`
- `UInt8` represents 256 values ranging from `0` to `255`
#### Overflow behavior
Signed and unsigned integers differ in how they handle overflow.
- When a signed integer overflows, the value wraps around into the negative
range using two's complement arithmetic. For example, adding `1` to
`var si: Int8 = 127` results in `-128`.
- When an unsigned integer overflows, the value wraps around to the beginning of
its range. For example, adding `1` to `var ui: UInt8 = 255` results in `0`.
You may prefer unsigned integers when negative values are not required, when
you are not designing a public API, or when you want to maximize the usable
positive range.
#### Mojo-supported fixed-width integer types
Table 1. Mojo signed integer types
| Type name | Description |
|-----------|------------------------|
| `Int8` | 8-bit signed integer |
| `Int16` | 16-bit signed integer |
| `Int32` | 32-bit signed integer |
| `Int64` | 64-bit signed integer |
| `Int128` | 128-bit signed integer |
| `Int256` | 256-bit signed integer |
Table 2. Mojo unsigned integer types
| Type name | Description |
|-----------|--------------------------|
| `UInt8` | 8-bit unsigned integer |
| `UInt16` | 16-bit unsigned integer |
| `UInt32` | 32-bit unsigned integer |
| `UInt64` | 64-bit unsigned integer |
| `UInt128` | 128-bit unsigned integer |
| `UInt256` | 256-bit unsigned integer |
### Floating-point numbers
Mojo provides several floating-point types for representing real numbers at
different precisions. Since floating-point values use a fixed number of bits,
some numbers can't be represented exactly.
The floating-point types `Float64`, `Float32`, and `Float16` follow the
IEEE 754-2008 standard for representing floating-point values. Each type
includes a sign bit, a set of bits representing an exponent, and a set of bits
representing the mantissa (also called fraction or significand).
Table 3 shows how these types are represented in memory.
Table 3. Details of floating-point types
| Type name | Sign | Exponent | Mantissa |
|-----------|-------|----------|----------|
| `Float64` | 1 bit | 11 bits | 52 bits |
| `Float32` | 1 bit | 8 bits | 23 bits |
| `Float16` | 1 bit | 5 bits | 10 bits |
Exponent values of all zeros or all ones represent special cases. These
patterns allow floating-point numbers to encode positive and negative
infinity, signed zeros, and not-a-number (NaN). These values are available
as static constants provided by
[`FloatLiteral`](/docs/std/builtin/float_literal/FloatLiteral/):
```mojo
from std.math import copysign
from std.utils.numerics import isfinite, isinf, isnan
var inf = FloatLiteral.infinity
print(isinf(inf)) # `True`
print(inf > 0) # `True`
var neginf = FloatLiteral.negative_infinity
print(isinf(neginf)) # `True`
print(neginf < 0) # `True`
var nan = FloatLiteral.nan
print(isnan(nan)) # `True`
var negzero = FloatLiteral.negative_zero
print(negzero == 0.0) # `True`
print(copysign(1.0, negzero) < 0) # `True`
```
For more details on how floating-point numbers are represented, see
[IEEE 754](https://en.wikipedia.org/wiki/IEEE_754).
#### Floating-point approximations and comparisons
Because floating-point values are approximate, they often cannot represent the
exact mathematical value they are intended to model.
- **Rounding errors.** Rounding may produce unexpected results. For example,
`1/3` cannot be represented exactly in floating-point formats. As more
floating-point operations are performed, rounding errors may accumulate.
- **Space between consecutive numbers.** The distance between consecutive
representable values varies across the range of a floating-point type. Near
zero, values are densely packed. For large positive or negative numbers, the
spacing can exceed 1, making it impossible to represent some consecutive
integers.
Because values are approximate, it is rarely useful to compare floating-point
numbers using the equality operator (`==`). For example:
```mojo
var big_num = 1.0e16
var bigger_num = big_num + 1.0
print(big_num == bigger_num)
```
```output
True
```
Comparison operators (such as `<` and `>=`) work as expected with floating-point
values. To test whether two values are equal within a tolerance,
use the [`math.isclose()`](/docs/std/math/math/isclose/) function to
compare whether two floating-point numbers are equal within a
specified tolerance.
#### Mojo-supported floating-point types
In the following table, the **eXmX** format (for example, `Float8_e5m2` and
`Float8_e4m3fn`) refers to the number of bits allocated to a floating-point
number's exponent and mantissa.
All IEEE 754 floating-point formats use an implied leading 1. That means
`Float32` is e8m23 but effectively e8m24, `Float16` is e5m10 but effectively
e5m11, `BFloat16` is e8m7 but effectively e8m8, and `Float8_e4m3fn` is e4m3 but
effectively e4m4.
In addition to eXmX:
- **fn** signifies finite numbers only. The numbers are valid floating-point
values that are not infinite. NaN is supported.
- **uz** means unsigned zero. Only +0 is supported, not -0.
Although Mojo supports all these types, these types are not supported
on all hardware.
:::note
The *B* in `BFloat16` stands for Brain, from the Google Brain artificial
intelligence research group. Google developed it specifically for their
Tensor Processing Units (TPUs) to accelerate machine learning workloads.
Therefore the B is a project identifier and not a technical format indicator.
:::
Table 4. Mojo floating-point types
| Type name | Description | CPU/GPU Support |
|-------------------|------------------------------------------------------------------------------------------------------------------------|-----------------|
| `Float16` | 16-bit floating-point(IEEE 754-2008 binary16) | CPU and GPU |
| `Float32` | 32-bit floating-point(IEEE 754-2008 binary32) | CPU and GPU |
| `Float64` | 64-bit floating-point(IEEE 754-2008 binary64) | CPU and GPU |
| `BFloat16` | 16-bit floating-point(16-bit version of IEEE 754 binary32) | CPU and GPU |
| `Float4_e2m1fn` | 4-bit floating-point(e2m1 format from Open Compute MX specification — finite values and NaN only, no infinities) | GPU |
| `Float8_e5m2` | 8-bit floating-point(OFP8 e5m2 format) | GPU |
| `Float8_e5m2fnuz` | 8-bit floating-point(AMD-only e5m2fnuz format — finite values and NaN only, no infinities) | GPU |
| `Float8_e4m3fn` | 8-bit floating-point(OFP8 e4m3fn format — finite values and NaN only, no infinities) | GPU |
| `Float8_e4m3fnuz` | 8-bit floating-point(AMD-only e4m3fnuz format — finite values and NaN only, no infinities) | GPU |
:::note
GPU-only floating-point types are supported on specific accelerator hardware and
may not be available on all GPUs.
:::
#### AI-optimized floating-point formats
Several floating-point types are specifically designed for AI and machine
learning workloads, trading precision for memory efficiency and computational
throughput.
**BFloat16 (Brain Floating Point)** uses the same 8 exponent bits as
`Float32`, preserving its dynamic range, but uses 7 explicit bits (8 effective
bits) for the mantissa compared to Float32's 23 bits.
This makes it ideal for neural network training where gradient magnitudes
vary widely but high precision is less critical.
**8-bit formats** (`Float8_e5m2`, `Float8_e4m3fn`, and their `fnuz`
variants) are ultra-compact formats for AI accelerators where memory
bandwidth is the primary bottleneck.
The naming indicates bit allocation: `e5m2` means 5 exponent bits and
2 mantissa bits.
The `fnuz` suffix additionally denotes unsigned zero (no -0), used
in AMD hardware.
**4-bit format** (`Float4_e2m1fn`) offers extreme compression with only 2
exponent bits and 1 mantissa bit, used in specialized inference scenarios
where accuracy can be traded for maximum throughput.
### Numeric literals
In addition to these numeric types, the standard libraries provides integer and
floating-point literal types,
[`IntLiteral`](/docs/std/builtin/int_literal/IntLiteral/) and
[`FloatLiteral`](/docs/std/builtin/float_literal/FloatLiteral/).
These literal types are used at compile time to represent literal numbers that
appear in the code. In general, you should never instantiate these types
yourself.
Table 5 summarizes the literal formats you can use to represent numbers.
Table 5. Numeric literal formats
| Format | Examples | Notes |
|------------------------|-----------------|--------------------------------------------------------------------------------------------------|
| Integer literal | `1760` | Integer literal, in decimal format. |
| Hexadecimal literal | `0xaa`, `0xFF` | Integer literal, in hexadecimal format.Hex digits are case-insensitive. |
| Octal literal | `0o77` | Integer literal, in octal format. |
| Binary literal | `0b0111` | Integer literal, in binary format. |
| Floating-point literal | `3.14`, `1.2e9` | Floating-point literal.Must include the decimal point to be interpreted as floating-point. |
At compile-time, Mojo treats numeric literals as arbitrary-precision values, so
the compiler can perform compile-time calculations without overflow or rounding
errors.
At runtime the values are converted to finite-precision types. `IntLiteral` can
convert to any finite-precision integer type, defaulting to `Int` if the type is
unspecified. And `FloatLiteral` converts to any finite-precision floating-point
type, defaulting to `Float64`.
```mojo
var float1 = 3.3 # float1 is type Float64
var float2: Float32 = 7.5
var int1 = 5 # int1 is type Int
var int2: Int8 = 4
```
This process of converting a value that can only exist at compile time into a
runtime value is called *materialization*.
The following code sample shows the difference between an arbitrary-precision
calculation and the same calculation done using `Float64` values at runtime,
which suffers from rounding errors.
```mojo
var arbitrary_precision = 3.0 * (4.0 / 3.0 - 1.0)
# use a variable to force the following calculation to occur at runtime
var three = 3.0
var finite_precision = three * (4.0 / three - 1.0)
print(arbitrary_precision, finite_precision)
```
```output
1.0 0.99999999999999978
```
### `SIMD` and `DType`
To support high-performance numeric processing, Mojo uses the
[`SIMD`](/docs/std/simd/SIMD/) type as the basis for its numeric
types. SIMD (single instruction, multiple data) is a processor technology that
allows you to perform an operation on an entire set of operands at once. Mojo's
`SIMD` type abstracts SIMD operations. A `SIMD` value represents a SIMD
*vector*—that is, a fixed-size array of values that can fit into a processor's
register. SIMD vectors are defined by two
[*parameters*](/docs/manual/parameters/):
- A `DType` value, defining the data type in the vector (for example,
32-bit floating-point numbers).
- The number of elements in the vector, which must be a power of two.
For example, you can define a vector of four `Float32` values like this:
```mojo
var vec = SIMD[DType.float32, 4](3.0, 2.0, 2.0, 1.0)
```
Math operations on SIMD values are
applied *elementwise*, on each individual element in the vector. For example:
```mojo
var vec1 = SIMD[DType.int8, 4](2, 3, 5, 7)
var vec2 = SIMD[DType.int8, 4](1, 2, 3, 4)
var product = vec1 * vec2
print(product)
```
```output
[2, 6, 15, 28]
```
### Scalar values
The `SIMD` module defines several [`comptime`
values](/docs/manual/metaprogramming/comptime-evaluation/#comptime-values)
that function as *type aliases*—shorthand names for different `SIMD` vector
types. The `Scalar` type is a `SIMD` vector with a single element. The
numeric types, including signed integers such as `Int8` ([Table
1](#table-1)), unsigned integers such as `UInt16` ([Table 2](#table-2)),
and floating-point values such as `Float32` ([Table 4](#table-4)), are type
aliases for scalar values:
```mojo
comptime Scalar = SIMD[length=1]
comptime Int = Scalar[DType.int]
comptime Int8 = Scalar[DType.int8]
comptime Float32 = Scalar[DType.float32]
```
This means that whether you're working with a single `Float32` value or a
vector of float32 values, the math operations go through exactly the same
code path.
#### The `DType` type
The `DType` struct describes the different data types that a `SIMD` vector can
hold, and defines a number of utility functions for operating on those data
types. The `DType` struct defines a set of
[`comptime` members](/docs/manual/parameters/#comptime-members) that act as
identifiers for the different data types, like `DType.uint` and `DType.float32`.
You use these `comptime` members when declaring a `SIMD` vector:
```mojo
var v: SIMD[DType.float64, 16]
```
Note that `DType.float64` isn't a *type*, it's a value that describes a data
type. You can't create a variable with the type `DType.float64`. You can create
a variable with the type `SIMD[DType.float64, 1]` (or `Float64`, which is the
same thing).
```mojo
from std.utils.numerics import max_finite, min_finite
def describeDType[dtype: DType]():
print(dtype, "is floating-point:", dtype.is_floating_point())
print(dtype, "is integral:", dtype.is_integral())
print("Min/max finite values for", dtype)
print(min_finite[dtype](), max_finite[dtype]())
describeDType[DType.float32]()
```
```output
float32 is floating-point: True
float32 is integral: False
Min/max finite values for float32
-3.4028234663852886e+38 3.4028234663852886e+38
```
There are several other data types in the standard library that also use
the `DType` abstraction.
### Numeric type conversion
In Mojo, numeric [operators](/docs/manual/operators/) **don't** automatically
narrow or widen operands to a common type. You need to explicitly convert the
operands to the desired type.
You can explicitly convert a `SIMD` value to a different `SIMD` type either by
invoking its [`cast()`](/docs/std/simd/SIMD/#cast) method or by passing
it as an argument to the initializer of the target type. For example:
```mojo
var simd1 = SIMD[DType.float32, 4](2.2, 3.3, 4.4, 5.5)
var simd2 = SIMD[DType.int16, 4](-1, 2, -3, 4)
var simd3 = simd1 * simd2.cast[DType.float32]() # Convert with cast() method
print("simd3:", simd3)
var simd4 = simd2 + SIMD[DType.int16, 4](
simd1
) # Convert with SIMD initializer
print("simd4:", simd4)
```
```output
simd3: [-2.2, 6.6, -13.200001, 22.0]
simd4: [1, 5, 1, 9]
```
You can convert a `Scalar` value by passing it as an argument to the initializer
of the target type. For example:
```mojo
var my_int: Int16 = 12 # SIMD[DType.int16, 1]
var my_float: Float32 = 0.75 # SIMD[DType.float32, 1]
var result = Float32(my_int) * my_float # Result is SIMD[DType.float32, 1]
print("Result:", result)
```
```output
Result: 9.0
```
You can convert a scalar value of any numeric type to `Int` by passing the value
to the [`Int()`](/docs/std/simd/SIMD/#__init__) initializer method.
Additionally, you can pass an instance of any struct that implements the
[`Intable`](/docs/std/builtin/int/Intable/) trait or
[`IntableRaising`](/docs/std/builtin/int/IntableRaising/) trait to the `Int()`
initializer to convert that instance to an `Int`.
## Strings
Strings are Mojo's primary text type. They store UTF-8 encoded text and
provide a safe, ergonomic interface for string manipulation.
Mojo's `String` type is a mutable string. `String` supports a variety
of operators and common methods:
```mojo
var s: String = "Testing"
s += " Mojo strings"
print(s) # Testing Mojo strings
```
### Construction
Many standard library types conform to the
[`Writable`](/docs/std/format/Writable/) trait, which indicates
that a value can be converted into a `String` using the `String(...)`
initializer.
The built-in [`print()`](/docs/std/io/io/print/) function accepts values
that conform to the `Writable` trait.
Use `String(value)` to explicitly convert a value to a `String`:
```mojo
var s = "Items in list: " + String(5)
print(s) # Items in list: 5
```
Or, use the string initializer with variadic `Writable` types,
so you don't have to call `String()` on each value:
```mojo
var s = String("Items in list: ", 5)
print(s) # Items in list: 5
```
### Emoji and grapheme clusters
Mojo source files are UTF-8, letting you write emoji and other non-ASCII
characters directly inside string literals.
```mojo
var wave = "👋"
```
The standard library counts emoji three different ways, and the answers
usually disagree:
- `byte_length()` returns the number of UTF-8 bytes.
- `count_codepoints()` counts the Unicode code points.
- `count_graphemes()` returns the number of user-perceived characters
(grapheme clusters), following
[UAX #29](https://www.unicode.org/reports/tr29/).
The grapheme count is what matches what a human would tell you if you
asked them to "count characters." A family emoji, a flag, and a waving
hand with a skin tone are each one grapheme, even though each is built
from several joined code points:
```mojo
def show(label: StaticString, s: StringSlice):
print(
label,
"bytes=", s.byte_length(),
"codepoints=", s.count_codepoints(),
"graphemes=", s.count_graphemes(),
)
def main():
show("family ", "👨👩👧👦")
show("flag ", "🇺🇸")
show("wave ", "👋🏽")
show("namaste ", "नमस्ते")
```
Output:
```text
family bytes=25 codepoints=7 graphemes=1
flag bytes=8 codepoints=2 graphemes=1
wave bytes=8 codepoints=2 graphemes=1
namaste bytes=18 codepoints=6 graphemes=3
```
If you'd rather not embed non-ASCII bytes in your source, for example, in
ASCII-only codebases, you can spell the code point with a hex escape or
`chr()`:
```mojo
var wave = "\U0001F44B" # 8-digit hex escape
var wave2 = chr(0x1F44B) # chr() function
var copy = "\u00A9" # 4-digit hex escape, ©
var euro = "\u20AC" # 4-digit hex escape, €
```
Mojo's 4-digit `\uHHHH` escape spells code points up to U+FFFF. The
8-digit `\U` form extends to U+10FFFF, Unicode's upper limit, covering
emoji like 👋 (U+1F44B) and other characters above the Basic Multilingual
Plane. The `chr()` function works for the full range too.
Both forms reject surrogate code points (U+D800 to U+DFFF). Surrogates
are reserved for UTF-16 encoding and aren't valid on their own. To
escape a character above U+FFFF, write its full code point with `\U`.
Don't use a UTF-16 surrogate pair.
### String formatting
The `format()` method inserts values into a string using manual or
automatic positional indexing. Replacement fields use braces:
```mojo
print("{0} {1} {0}".format("Mojo", 1.125)) # Mojo 1.125 Mojo
print("{} {}".format(True, "hello world")) # True hello world
```
Mojo's `TString` (template string) replaces the functionality of
the `format()` method with direct expressions and better
performance characteristics.
`TString`s work like `format()`, but they insert
[`Writable`](/docs/std/format/Writable/) representations of expressions
into replacement fields. This provides safe and flexible string
processing:
```mojo
var count = 3
var items = "apples"
var template = t"Give me {count} {items}." # Template string
print(template) # Output: Give me 3 apples.
```
`TString` values are lazy. They don't allocate until you explicitly
construct a `String`:
```mojo
var x = 41
print(t"The answer is {x + 1}") # The answer is 42 (no allocation)
var name = "Nate"
var template = t"Hello, {name}!" # template creation
print(template) # Hello, Nate! (no allocation)
var s = String(template) # explicitly construct a string
```
`TString`s can add arbitrary expressions within the replacement fields:
```mojo
var list: List[Int] = [1, 2, 3]
print(t"{list[0] + list[1]}") # 3
```
### Raw strings
In Mojo, **raw strings** are string literals prefixed with `r`.
If you ran the following command it would print on one line, not
two, because raw strings prevent backslash escape sequences from
being interpreted:
```mojo
print(r"Hello\nWorld") # Hello\nWorld, with the backslash and n
```
Raw strings help with regular expressions, code generation,
serialization code, and other applications where you want to use
escape sequences as literal entries in your string. Unlike
normal strings, escapes aren't processed.
Rawness applies to all forms of strings: single line, multi-line,
docstrings, and `TString`s. Raw `TString`s support interpolation
but won't expand escape sequences:
```mojo
var name = "Nate"
print(rt"Hello,\t{name}.") # Hello,\tNate., with the backslash and t
```
Raw strings *still* need a way to terminate, so if you have to use
`"` within your content, use an alternate form of quote, such as
single quote (`'`) or triple quotes, (`"""`, `'''`) to enclose
it:
```mojo
r'She said, "Hello, World!"'
```
### String literals
As with numeric types, the standard library includes a string literal type used
to represent literal strings in the program source. String literals are
enclosed in either single or double quotes.
Adjacent literals are concatenated together, so you can define a long string
using a series of literals broken up over several lines:
```mojo
comptime s = "A very long string which is "
"broken into two literals for legibility."
```
To define a multi-line string, enclose the literal in three single or double
quotes:
```mojo
comptime s = """
Multi-line string literals let you
enter long blocks of text, including
newlines."""
```
Note that the triple double quote form is also used for API documentation
strings.
A `StringLiteral` will materialize to a `String` when used at run-time:
```mojo
comptime param = "foo" # type = StringLiteral
var runtime_value = "bar" # type = String
var runtime_value2 = param # type = String
```
## Booleans
Mojo's `Bool` type represents a boolean value. It can take one of two values,
`True` or `False`. You can negate a boolean value using the `not` operator.
```mojo
var conditionA = False
var conditionB: Bool
conditionB = not conditionA
print(conditionA, conditionB)
```
```output
False True
```
Many types have a boolean representation. Any type that implements the
[`Boolable`](/docs/std/builtin/bool/Boolable/) trait has a boolean
representation. As a general principle, collections evaluate as True if they
contain any elements, False if they are empty; strings evaluate as True if they
have a non-zero length.
## Tuples
Mojo's `Tuple` is a lightweight, fixed-size, heterogeneous collection with
value semantics. A tuple contains zero or more comma-separated values,
which may have different types. Although a tuple's structure (its size and
element types) is fixed, individual elements can be mutated. Tuples support
several forms of indexing:
```mojo
# Tuples can hold multiple types
var example_tuple = Tuple[Int, String](1, "Example")
# Assign multiple variables at once
var x, y = example_tuple
print(x, y)
# Get individual values with an index
var s = example_tuple[1]
print(s)
```
```output
1 Example
Example
```
You can also create a tuple without explicit typing.
```mojo
var example_tuple = (1, "Example")
var s = example_tuple[1]
print(s)
```
```output
Example
```
## Collection types
The Mojo standard library also includes a set of basic collection types that
can be used to build more complex data structures:
- [`List`](/docs/std/collections/list/List/), a dynamically-sized array of
items.
- [`Dict`](/docs/std/collections/dict/Dict/), an associative array of
key-value pairs.
- [`Set`](/docs/std/collections/set/Set/), an unordered collection of unique
items.
- [`Optional`](/docs/std/collections/optional/Optional/)
represents a value that may or may not be present.
The collection types are *parameterized types*: while a given collection can
only hold a specific type of value (such as `Int` or `Float64`), you specify the
type at compile time using a [parameter](/docs/manual/parameters/). For example,
you can create a `List` of `Int` values like this:
```mojo
var l: List[Int] = [1, 2, 3, 4]
# l.append(3.14) # error: FloatLiteral cannot be converted to Int
```
You don't always need to specify the type explicitly. If Mojo can *infer* the
type, you can omit it. For example, when you construct a list from a set of
integer literals, Mojo creates a `List[Int]`.
```mojo
# Inferred type == List[Int]
var l1: List = [1, 2, 3, 4]
```
Where you need a more flexible collection, the
[`Variant`](/docs/std/utils/variant/Variant/) type can hold different types
of values. For example, a `Variant[Int32, Float64]` can hold either an `Int32`
*or* a `Float64` value at any given time. (Using `Variant` is not covered in
this section, see the [API docs](/docs/std/utils/variant/Variant/) for more
information.)
The following sections give brief introduction to the main collection types.
### List
[`List`](/docs/std/collections/list/List/) is a dynamically-sized array of
elements. You can create a `List` by passing the element type as a
parameter, like this:
```mojo
var l = List[String]()
```
The `List` type supports a subset of the Python `list` API, including the
ability to append to the list, pop items out of the list, and access list items
using subscript notation.
```mojo
var list: List[Int] = [2, 3, 5]
list.append(7)
list.append(11)
print("Popping last item from list: ", list.pop())
for idx in range(len(list)):
print(list[idx], end=", ")
```
```output
Popping last item from list: 11
2, 3, 5, 7,
```
Note that the previous code sample leaves out the type parameter when creating
the list. Because the list is being created with a set of `Int` values, Mojo can
*infer* the type from the arguments.
- Mojo supports list, set, and dictionary literals for collection
initialization:
```mojo
# List literal, element type infers to Int.
var nums: List = [2, 3, 5]
```
You can also use an explicit type if you want a specific element type:
```mojo
var list : List[UInt8] = [2, 3, 5]
```
You can also use list "comprehensions" for compact conditional initialization:
```mojo
var list2 = [x*Int(y) for x in nums for y in list if x != 3]
```
- You can't `print()` a list, or convert it directly into a string.
```mojo
# Does not work
print(list)
```
As shown above, you can print the individual elements in a list as long as
they're a [`Writable`](/docs/std/format/Writable/) type.
- Iterating a `List` returns an immutable
[reference](/docs/manual/values/lifetimes/#working-with-references) to each
item:
```mojo
var list: List[Int] = [2, 3, 4]
for item in list:
print(item, end=", ")
```
```output
2, 3, 4,
```
If you would like to mutate the elements of the list, capture the reference to
the element with `ref` instead of making a copy:
```mojo
var list: List[Int] = [2, 3, 4]
for ref item in list: # Capture a ref to the list element
print(item, end=", ")
item = 0 # Mutates the element inside the list
print("\nAfter loop:", list[0], list[1], list[2])
```
```output
2, 3, 4,
After loop: 0 0 0
```
You can see that the original loop entries were modified.
### Dict
The [`Dict`](/docs/std/collections/dict/Dict/) type is an associative array
that holds key-value pairs. You can create a `Dict` by specifying the key type
and value type as parameters and using dictionary literals:
```mojo
# Empty dictionary
var empty_dict: Dict[String, Float64] = {}
# Dictionary with initial key-value pairs
var values: Dict[String, Float64] = {"pi": 3.14159, "e": 2.71828}
```
You can also use the initializer syntax:
```mojo
var values = Dict[String, Float64]()
```
The dictionary's key type must conform to the
[`KeyElement`](/docs/std/collections/dict/#keyelement) trait, and value
elements must conform to the
[`Copyable`](/docs/std/traits/copyable/Copyable/) trait.
You can insert and remove key-value pairs, update the value assigned to a key,
and iterate through keys, values, or items in the dictionary.
The `Dict` iterators all yield
[references](/docs/manual/values/lifetimes/#working-with-references), which are
copied into the declared name by default, but you can use the `ref` marker to
avoid the copy:
```mojo
var d: Dict[String, Float64] = {
"plasticity": 3.1,
"elasticity": 1.3,
"electricity": 9.7
}
for item in d.items():
print(item.key, item.value)
```
```output
plasticity 3.1000000000000001
elasticity 1.3
electricity 9.6999999999999993
```
This is an unmeasurable micro-optimization in this case, but is useful when
working with types that aren't `Copyable`.
### Set
The [`Set`](/docs/std/collections/set/Set/) type represents a set of unique
values. You can add and remove elements from the set, test whether a value
exists in the set, and perform set algebra operations, like unions and
intersections between two sets.
Sets are parameterized and the element type must conform to the
[`KeyElement`](/docs/std/collections/dict/#keyelement) trait. Like lists and
dictionaries, sets support standard literal syntax, as well as generator
comprehensions:
```mojo
var i_like = {"sushi", "ice cream", "tacos", "pho"}
var you_like = {"burgers", "tacos", "salad", "ice cream"}
var we_like = i_like.intersection(you_like)
print("We both like:")
for item in we_like:
print("-", item)
```
```output
We both like:
- ice cream
- tacos
```
### Optional
An [`Optional`](/docs/std/collections/optional/Optional/) represents a
value that may or may not be present. Like the other collection types, it is
parameterized, and can hold any type that conforms to the
[`Copyable`](/docs/std/traits/copyable/Copyable/) trait.
```mojo
# Two ways to initialize an Optional with a value
var opt1 = Optional(5)
var opt2: Optional[Int] = 5
# Two ways to initialize an Optional with no value
var opt3 = Optional[Int]()
var opt4: Optional[Int] = None
```
An `Optional` evaluates as `True` when it holds a value, `False` otherwise. If
the `Optional` holds a value, you can retrieve a reference to the value using
the `value()` method. But calling `value()` on an `Optional` with no value
results in undefined behavior, so you should always guard a call to `value()`
inside a conditional that checks whether a value exists.
```mojo
var opt: Optional[String] = "Testing"
if opt:
var value_ref = opt.value()
print(value_ref)
```
```output
Testing
```
Alternately, you can use the `or_else()` method, which returns the stored
value if there is one, or a user-specified default value otherwise:
```mojo
var custom_greeting: Optional[String] = None
print(custom_greeting.or_else("Hello")) # Hello
custom_greeting = "Hi"
print(custom_greeting.or_else("Hello")) # Hi
```
---
## Intro to value ownership
A program is nothing without data, and all modern programming languages store
data in one of two places: the call stack and the heap (also sometimes in CPU
registers, but we won't get into that here). However, each language reads and
writes data a bit differently—sometimes very differently. So in the following
sections, we'll explain how Mojo manages memory in your programs and how this
affects the way you write Mojo code.
## Stack and heap overview
In general, all modern programming languages divide a running program's memory
into four segments:
- Text. The compiled program.
- Data. Global data, either initialized or uninitialized.
- Stack. Local data, automatically managed during the program's runtime.
- Heap. Dynamically-allocated data, managed by the programmer.
The text and data segments are statically sized, but the stack and heap change
size as the program runs.
The *stack* stores data local to the current function. When a function is
called, the program allocates a block of memory—a *stack frame*—that is exactly
the size required to store the function's data, including any *fixed-size*
local variables. When another function is called, a new stack frame is pushed
onto the top of the stack. When a function is done, its stack frame is popped
off the stack.
Notice that we said only "*fixed-size* local values" are stored in the stack.
Dynamically-sized values that can change in size at runtime are instead stored
in the heap, which is a much larger region of memory that allows for dynamic
memory allocation. Technically, a local variable for such a value is still
stored in the call stack, but its value is a fixed-size pointer to the real
value on the heap. Consider a Mojo string: it can be any length, and its length
can change at runtime. So the Mojo `String` struct includes some
statically-sized fields, plus a pointer to a dynamically-allocated buffer
holding the actual string data.
Another important difference between the heap and the stack is that the stack is
managed automatically—the code to push and pop stack frames is added by the
compiler. Heap memory, on the other hand, is managed by the programmer
explicitly allocating and deallocating memory. You may do this indirectly—by
using standard library types like `List` and `String`—or directly, using the
[`alloc()`](/docs/std/memory/alloc/alloc/) and
[`Pointer`](/docs/std/memory/pointer/Pointer/) APIs.
Values that need to outlive the lifetime of a function (such as
an array that's passed between functions and should not be copied) are stored
in the heap, because heap memory is accessible from anywhere in the call stack,
even after the function that created it is removed from the stack. This sort of
situation—in which a heap-allocated value is used by multiple functions—is where
most memory errors occur, and it's where memory management strategies vary the
most between programming languages.
## Memory management strategies
Because memory is limited, it's important that programs remove unused data from
the heap ("free" the memory) as quickly as possible. Figuring out when to free
that memory is pretty complicated.
Some programming languages try to hide the complexities of memory management
from you by utilizing a "garbage collector" process that tracks all memory
usage and deallocates unused heap memory periodically (also known as automatic
memory management). A significant benefit of this method is that it relieves
developers from the burden of manual memory management, generally avoiding more
errors and making developers more productive. However, it incurs a performance
cost because the garbage collector interrupts the program's execution, and it
might not reclaim memory very quickly.
Other languages require that you manually free data that's allocated on the
heap. When done properly, this makes programs execute quickly, because there's
no processing time consumed by a garbage collector. However, the challenge with
this approach is that programmers make mistakes, especially when multiple parts
of the program need access to the same memory—it becomes difficult to know
which part of the program "owns" the data and must deallocate it. Programmers
might accidentally deallocate data before the program is done with it (causing
"use-after-free" errors), or they might deallocate it twice ("double free"
errors), or they might never deallocate it ("leaked memory" errors). Mistakes
like these and others can have catastrophic results for the program, and these
bugs are often hard to track down, making it especially important that they
don't occur in the first place.
Mojo uses a third approach called "ownership" that relies on a collection of
rules that programmers must follow when passing values. The rules ensure there
is only one "owner" for a given value at a time. When a value's lifetime ends,
Mojo calls its deinitializer, which is responsible for deallocating any
heap memory that needs to be deallocated.
In this way, Mojo helps ensure memory is freed, but it does so in a way
that's deterministic and safe from errors such as use-after-free,
double-deallocation and memory leaks. Plus, it does so with a very low
performance overhead.
Mojo's value ownership model provides an excellent balance of programming
productivity and strong memory safety. It only requires that you learn some new
syntax and a few rules about how to share access to memory within your program.
But before we explain the rules and syntax for Mojo's value ownership model,
you first need to understand [value
semantics](/docs/manual/values/value-semantics).
---
## Lifetimes, origins, and references
The Mojo compiler includes a lifetime checker, a compiler pass that analyzes
dataflow through your program. It identifies when variables are valid and
inserts deinitializer calls when a variable's lifetime ends.
The Mojo compiler uses a special value called an *origin* to track the lifetime
of variables and the validity of references.
Specifically, an origin answers two questions:
- What variable "owns" this value?
- Can the value be mutated using this reference?
For example, consider the following code:
```mojo
def print_str(s: String):
print(s)
def main():
var name: String = "Joan"
print_str(name)
```
```output
Joan
```
The line `name = "Joan"` declares a variable with an identifier (`name`)
and logical storage space for a `String` value. When you pass `name` into the
`print_str()` function, the function gets an immutable reference to the value.
So both `name` and `s` refer to the same logical storage space, and have
associated origin values that lets the Mojo compiler reason about them.
Origin tracking and lifetime checking is done at compile time, so origins don't
track the actual storage space allocated for the `name` variable, for example.
Instead, origins track variables symbolically, so the compiler tracks that
`print_str()` is called with a value owned by `name` in the caller's scope. By
tracking how owned data flows through the program, the compiler can identify
the lifetimes of values.
Most of the time, origins are handled automatically by the compiler.
However, in some cases you'll need to interact with origins directly:
- When working with references—specifically `ref` arguments and `ref` return
values.
- When working with types like
[`Pointer`](/docs/std/memory/pointer/Pointer/) or
[`Span`](/docs/std/collections/span/Span/) which are parameterized on the
origin of the data they refer to.
This section also covers [`ref` arguments](#ref-arguments) and
[`ref` return values](#ref-return-values), which let functions take arguments
and provide return values as references with parametric origins.
## Working with origins
Mojo's origin values are mostly created by the
compiler, so you can't just create your own origin value—you usually need to
derive an origin from an existing value.
Among other things, Mojo uses origins to extend the lifetimes of referenced
values, so values aren't destroyed prematurely.
### Origin types
Mojo supplies a struct and a set of type aliases (`comptime` values) that you
can use to specify origin types. As the names suggest, the `ImmOrigin` and
`MutOrigin` `comptime` values represent immutable and mutable origins,
respectively:
```mojo
struct ImmutRef[origin: ImmOrigin]:
pass
```
Or you can use the [`Origin`](/docs/std/origin/Origin/)
struct to specify an origin with parametric mutability:
```mojo
struct ParametricRef[
is_mutable: Bool,
//,
origin: Origin[mut=is_mutable]
]:
pass
```
Origin types carry the mutability of a reference as a boolean parameter value,
indicating whether the origin is mutable, immutable, or even with mutability
depending on a parameter specified by the enclosing API.
The `is_mutable` parameter here is an [infer-only
parameter](/docs/manual/parameters/#infer-only-parameters). The `origin` value
is often inferred, as well. For example, the following code creates a
[`Pointer`](/docs/std/memory/pointer/Pointer/) to an existing value, but
doesn't need to specify an origin—the `origin` is inferred from the existing
value.
```mojo
from std.memory import Pointer
def use_pointer():
var a = 10
var ptr = Pointer(to=a)
```
### Origin sets
An `OriginSet` is not a type of origin, it represents a group of origins. Origin
sets are used for tracking the lifetimes of values captured in parametric
closures.
An `OriginSet` **isn't** a general-purpose mechanism for expressing a
combination of multiple origins. Instead, you can use `origin_of()` to express
an [origin union](#origin-unions).
### Origin values
Most origin values are created by the compiler. As a developer, there are a
few ways to specify origin values:
- Static origin. The `ImmStaticOrigin` `comptime` value
represents immutable values that last for the duration of the program.
String literal values have a `ImmStaticOrigin`.
- Derived origin. The `origin_of()` magic function returns the origin
associated with the value (or values) passed in.
- Inferred origin. You can use inferred parameters to capture the origin of a
value passed in to a function.
- Untracked origins. The untracked origins, `MutUntrackedOrigin` and
`ImmUntrackedOrigin` represent values that are not tracked by the lifetime
checker, such as dynamically-allocated memory.
- Wildcard origins. The `ImmUnsafeAnyOrigin` and `MutUnsafeAnyOrigin`
`comptime` values are special cases indicating a reference that might access
any live value.
#### Static origins
You can use the static origin `ImmStaticOrigin` when you have a
value that exists for the entire duration of the program.
For example, the `StringLiteral` method
[`as_string_slice()`](/docs/std/builtin/string_literal/StringLiteral/#as_string_slice)
returns a [`StringSpan`](/docs/std/collections/string/string_span/StringSpan/)
pointing to the original string literal. String literals are static—they're
allocated at compile time and never destroyed—so the slice is created with an
immutable, static origin.
#### Derived origins
Use the `origin_of(value)` operator to obtain a value's origin. An argument
to `origin_of()` can take an arbitrary expression that yields one of the
following:
- An origin value.
- A value with a memory location.
For example:
```mojo
origin_of(self)
origin_of(x.y)
origin_of(foo())
```
The `origin_of()` operator is analyzed statically at compile time;
The expressions passed to `origin_of()` are never evaluated. (For example,
when the compiler analyzes `origin_of(foo())`, it doesn't run the `foo()`
function.)
The following struct stores a string value using a
[`OwnedPointer`](/docs/std/memory/owned_pointer/OwnedPointer/): a smart
pointer that holds an owned value. The `as_ptr()` method returns a `Pointer` to
the stored string, using the same origin as the original `OwnedPointer`.
```mojo
from std.memory import OwnedPointer, Pointer
struct BoxedString:
var o_ptr: OwnedPointer[String]
def __init__(out self, value: String):
self.o_ptr = OwnedPointer(value)
def as_ptr(mut self) -> Pointer[String, origin_of(self.o_ptr)]:
return Pointer(to=self.o_ptr[])
```
Note that the `as_ptr()` method takes its `self` argument as `mut self`. If it
used the default argument convention, it would be immutable, and the
derived origin (`origin_of(self.o_ptr)`) would also be immutable.
You can also pass multiple expressions to `origin_of()` to express the union
of two or more origins:
`origin_of(a, b)`
#### Origin unions
When a function returns a reference or pointer that can have one of several
different origins, you can express the referenced origin as a union of all
of the possible origin values.
The union of two or more origins creates a new origin that references all of the
original origins for the purposes of lifetime extension (so a union of the
origins of `a` and `b` extends both lifetimes). An origin union is mutable if
and only if all of its constituent origins are mutable. Use an origin union
For an example, see
[Return values with union origins](#return-values-with-union-origins).
#### Inferred origins
Since origins are parameters, the compiler can *infer* an origin value from
the argument passed to a function or method, as described in
[Parameter inference](/docs/manual/parameters/#parameter-inference). This allows
a function to return a value that has the same origin as the argument passed to
it.
See the section on [`ref` arguments](#ref-arguments) for an example using an
inferred origin.
#### Untracked origins
The untracked origins, `MutUntrackedOrigin` and `ImmUntrackedOrigin` represent
values that do not alias any existing value. That is, they point to memory that
is not owned by any other variable, and are therefore not tracked by the
lifetime checker. For example, the
[`alloc()`](/docs/std/memory/alloc/alloc/) function returns an
`Allocation` for a new dynamically-allocated block of memory, with the origin
`MutUntrackedOrigin`. The origin indicates that the memory is not managed by the
Mojo ownership system. When you use an unsafe API like this, you're responsible
for managing the lifetime yourself: for example, a struct that allocates memory
should generally free that memory in its deinitializer.
#### Wildcard origins
The wildcard origins, `ImmUnsafeAnyOrigin` and `MutUnsafeAnyOrigin`, are
special cases indicating a reference that might access any live value. These
were previously widely used for unsafe pointers. Using a pointer with a wildcard
origin into a scope effectively disables Mojo's ASAP destruction for any values
in that scope, as long as the pointer is live. It also prevents Mojo from
enforcing
[argument exclusivity](/docs/manual/values/ownership/#argument-exclusivity) and
hides unused variable warnings.
Accordingly, the use of wildcard origins is discouraged, and should be used as a
last resort.
## Working with references
You can use the `ref` keyword with arguments and return values to specify a
reference with parametric mutability. That is, they can be either mutable or
immutable.
A `ref` return value looks like any other return value to the calling function,
but it's a *reference* to an existing value, not a copy.
### `ref` arguments
The `ref` argument convention lets you specify an argument of parametric
mutability: that is, you don't need to know in advance whether the passed
argument will be mutable or immutable. There are several reasons you might want
to use a `ref` argument:
- You want to accept an argument with parametric mutability.
- You want to tie the lifetime of one argument to the lifetime of another
argument.
- When you want an argument that is guaranteed to be passed in memory: this
can be useful for parameterized arguments that need an identity, whether
or not the concrete type is register passable.
The syntax for a `ref` argument is:
ref arg_name : arg_type
Or:
ref[origin_specifier(s) ]
arg_name : arg_type
In the first form, the origin and mutability of the `ref` argument is inferred
from the value passed in. The second form includes an origin clause, consisting
of one or more origin specifiers inside square brackets. An origin
specifier can be either:
- An origin value.
- An arbitrary expression, which is treated as shorthand for
`origin_of(expression)`. In other words, the following declarations are
equivalent:
```mojo
ref[origin_of(self)]
ref[self]
```
- An [`AddressSpace`](/docs/std/memory/address_space/AddressSpace/) value.
- An underscore character (`_`) to indicate that the origin is *unbound*. This
is equivalent to omitting the origin specifier.
```mojo
def add_ref(ref a: Int, b: Int) -> Int:
return a+b
```
You can also name the origin explicitly. This is useful if you want to
restrict the argument to either a `ImmOrigin` or `MutOrigin`, or if you
want to bind a function's return value to the origin of an argument.
For example, the `Span` type is a non-owning view of contiguous data (like
a substring of a string, or a subset of a list). Because it points to data
that it doesn't own, it is parameterized on an origin value that represents
the lifetime and ownership of the data it points to.
In the following example, the `to_byte_span()` function takes a
`List[Byte]` and returns a `Span[Byte]` with the same origin as the list:
```mojo
from std.collections import List, Span
def to_byte_span[
is_mutable: Bool,
//,
origin: Origin[mut=is_mutable],
](ref[origin] list: List[Byte]) -> Span[Byte, origin]:
return Span(list)
def main():
var list: List[Byte] = [77, 111, 106, 111]
_ = to_byte_span(list)
```
In this example, the `origin` parameter is inferred from the `list` argument,
and then used as the origin for the returned `Span`.
Since the `Span` takes on the origin of the `list` argument, the Mojo compiler
can identify the span's data as owned by the list. The span will have the same
lifetime as the list, and the span will be mutable if the list is mutable.
### `ref` return values
Like `ref` arguments, `ref` return values allow a function to return a mutable
or immutable reference to a value. The syntax for a `ref` return value is:
-> ref[origin_specifier(s) ]
arg_type
Note that you **must** provide an origin specifier for a `ref` return value. The
values allowed for origin specifiers are the same as the ones listed for
[`ref` arguments](#ref-arguments).
`ref` return values can be an efficient way to handle updating items in a
collection. The standard way to do this is by implementing the `__getitem__()`
and `__setitem__()` dunder methods. These are invoked to read from and write to
a subscripted item in a collection:
```mojo
var value = list[a]
list[b] += 10
```
With a `ref` argument, `__getitem__()` can return a mutable reference that can
be modified directly. This has pros and cons compared to using a `__setitem__()`
method:
- The mutable reference is more efficient—a single update isn't broken up across
two methods. However, the referenced value must be in memory.
- A `__getitem__()`/`__setitem__()` pair allows for arbitrary code to be run
when values are retrieved and set. For example, `__setitem__()` can validate
or constrain input values.
For example, in the following example, `NameList` has a `__getitem__()` method
that returns a reference:
```mojo
struct NameList:
var names: List[String]
def __init__(out self, *names: String):
self.names = []
for name in names:
self.names.append(name)
def __getitem__(ref self, index: Int) raises -> ref[self.names[0]] String:
if (index >=0 and index < len(self.names)):
return self.names[index]
else:
raise Error("index out of bounds")
def main() raises:
var list = NameList("Thor", "Athena", "Dana", "Vrinda")
ref name = list[2]
print(name)
name += "?"
print(list[2])
```
```output
Dana
Dana?
```
Note the use of the `ref name` syntax to create a reference binding.
If you assign a `ref` return value to a variable, the variable receives a
*copy* of the referenced item. Use a
[reference binding](/docs/manual/variables/#reference-bindings) if you need to
capture the reference for future use:
```mojo
var name_copy = list[2] # owned copy of list[2]
ref name_ref = list[2] # reference to list[2]
```
#### Parametric mutability of return values
Another advantage of `ref` return arguments is the ability to support parametric
mutability. For example, recall the signature of the `__getitem__()` method
above:
```mojo
def __getitem__(ref self, index: Int) raises -> ref[self] String:
```
Since the `origin` of the return value is tied to the origin of `self`, the
returned reference will be mutable if the method was called using a
mutable reference. The method still works if you have an immutable reference
to the `NameList`, but it returns an immutable reference:
```mojo
def pass_immutable_list(list: NameList) raises:
print(list[2])
# list[2] += "?" # Error, this list is immutable
def main() raises:
var list = NameList("Sophie", "Jack", "Diana")
pass_immutable_list(list)
```
```output
Diana
```
Without parametric mutability, you'd need to write two versions of
`__getitem__()`, one that accepts an immutable `self` and another that accepts
a mutable `self`.
#### Return values with union origins
A `ref` return value can include multiple values in its origin specifier, which
yields the union of the origins. For example, the following `pick_one()`
function returns a reference to one of the two input strings, with an origin
that's a union of both origins.
```mojo
def pick_one(cond: Bool, ref a: String, ref b: String) -> ref[a, b] String:
return a if cond else b
```
Because the compiler can't statically determine which branch will be picked,
this function must use the union origin `[a, b]`. This ensures that the compiler
extends the lifetime of *both* values as long as the returned reference is live.
The returned reference is mutable if **both** `a` and `b` are mutable.
---
## Ownership
A challenge you might face when using some programming languages is that you
must manually allocate and deallocate memory. When multiple parts of the
program need access to the same memory, it becomes difficult to keep track of
who "owns" a value and determine when is the right time to deallocate it. If
you make a mistake, it may result in a "use-after-free" error, a "double free"
error, or a "leaked memory" error, any one of which can be catastrophic.
Mojo helps avoid these errors by ensuring there is only one variable that owns
each value at a time, while still allowing you to share references with other
functions. When the life span of the owner ends, Mojo
[destroys the value](/docs/manual/lifecycle/death). Programmers are still
responsible for making sure any type that allocates resources (including memory)
also deallocates those resources in its deinitializer. Mojo's ownership system
ensures that deinitializers are called promptly.
On this page, we'll explain the rules that govern this ownership model, and how
to specify different argument conventions that define how values are passed into
functions.
## Ownership summary
The fundamental rules that make Mojo's ownership model work are the following:
- Every value has only one owner at a time.
- When the lifetime of the owner ends, Mojo destroys the value.
- If there are existing references to a value, Mojo extends the lifetime of
the owner.
### Variables and references
A variable *owns* its value. A struct owns its fields.
A *reference* allows you to access a value owned by another variable. A
reference has either mutable access or immutable access to that value.
Mojo references are created when you call a function: function arguments are
passed as mutable or immutable references. A function can return a
reference instead of returning a value. To capture a returned reference, you
can use a reference binding:
```mojo
ref value_ref = list[0]
```
## Argument conventions
In all programming languages, code quality and performance is heavily dependent
upon how functions treat argument values. That is, whether a value received by
a function is a unique value or a reference, and whether it's mutable or
immutable, has a series of consequences that define the readability,
performance, and safety of the language.
In Mojo, we want to provide full [value
semantics](/docs/manual/values/value-semantics) by default, which provides
consistent and predictable behavior. But as a systems programming language, we
also need to offer full control over memory optimizations, which generally
requires reference semantics. The trick is to introduce reference semantics in
a way that ensures all code is memory safe by tracking the lifetime of every
value and destroying each one at the right time (and only once). All of this is
made possible in Mojo through the use of argument conventions that ensure every
value has only one owner at a time.
An argument convention specifies whether an argument is mutable or immutable,
and whether the function owns the value. Each convention is defined by a
keyword at the beginning of an argument declaration:
- default: The function receives an **immutable reference**. This means the
function can read the original value (it's *not* a copy), but it can't
mutate (modify) it.
- `mut`: The function receives a **mutable reference**. This means the
function can read and mutate the original value (it's *not* a copy).
- `var`: The function takes **ownership** of a value. This means the function
has exclusive ownership of the argument. The caller might choose to transfer
ownership of an existing value to this function, but that's not always what
happens. The callee might receive a newly-created value, or a copy of an
existing value.
- `ref`: The function gets a reference with a parametric mutability: that is,
it follows the mutability of the referenced value.
`ref` arguments are an advanced topic, and they're described in more detail in
[Lifetimes, origins, and references](/docs/manual/values/lifetimes/).
- `out`: A special convention used for the `self` argument in
[initializers](/docs/manual/lifecycle/life/#constructor) and for
[named results](/docs/manual/functions/#named-results). An `out`
argument is uninitialized at the beginning of the function, and must be
initialized before the function returns. Although `out` arguments show up in
the argument list, they're never passed in by the caller.
- `deinit`: A special convention used in the deinitializer and consuming-move
lifecycle methods. A `deinit` argument is initialized at the beginning of the
function, and uninitialized when the function returns.
For example, this function has one argument that's a mutable
reference and one that's immutable:
```mojo
def add(mut x: Int, y: Int):
x += y
def main():
var a = 1
var b = 2
add(a, b)
print(a) # 3
```
You've probably already seen some function arguments that don't declare a
convention. By default, all arguments use the default convention of an
immutable read-only reference. In the following sections, we'll explain
each of these conventions in more detail.
### Deinitializing arguments (`deinit`)
The `deinit` convention isn't limited to `self`. You can write methods
and functions that destruct other instances:
```mojo
struct Pair:
def destroy_other(self, deinit other: Self):
# Can take from fields of `other` here
```
Like `deinit self`, the `deinit` convention in this example tells the
compiler that `other` is tagged for destruction.
Using `deinit` means `other` is logically deinitialized at the end of the
method. Because of this, it's safe to move values out of `other`, since
the instance's lifetime is guaranteed to complete.
## Immutable arguments (default)
The default convention is an immutable read-only reference. The callee
receives an immutable reference to the argument value.
For example:
```mojo
def print_list(list: List[Int]):
print(list.__str__())
def main():
var values: List[Int] = [1, 2, 3, 4]
print_list(values)
```
```output
[1, 2, 3, 4]
```
Here the `print_list()` function can read from the `list` argument, but not
mutate it. `list` is a reference to `values` in the `main()` function, not a
copy.
In general, passing an immutable reference is much more efficient
when handling large or expensive-to-copy values, because the copy initializer
and deinitializer aren't invoked for a default (immutable reference) argument.
### Compared to C++ and Rust
Mojo's default argument convention is similar in some ways to passing an
argument by `const&` in C++, which also avoids a copy of the value and disables
mutability in the callee. However, the default convention differs from
`const&` in C++ in two important ways:
- The Mojo compiler implements a lifetime checker that ensures that values are
not destroyed when there are outstanding references to those values.
- Small values like `Int`, `Float`, and `SIMD` are always passed in
machine registers. This provides a significant performance enhancement
compared to languages like C++ and Rust.
The major difference between Rust and Mojo is that Mojo doesn't require a
sigil on the caller side to pass by immutable reference. Also, Mojo is more
efficient when passing small values, and Rust defaults to moving values
instead of passing them around as a read-only reference. These policy and
syntax decisions allow Mojo to provide an easier-to-use programming model.
## Mutable arguments (`mut`)
If you'd like your function to receive a **mutable reference**, add the `mut`
keyword in front of the argument name. You can think of `mut` like this: it
means any changes to the value *in*side the function are visible *out*side the
function.
For example, this `mutate()` function updates the original `list` value:
```mojo
def print_list(list: List[Int]):
print(list.__str__())
def mutate(mut l: List[Int]):
l.append(5)
def main():
var values: List[Int] = [1, 2, 3, 4]
mutate(values)
print_list(values)
```
```output
[1, 2, 3, 4, 5]
```
That behaves like an optimized replacement for this:
```mojo
def print_list(list: List[Int]):
print(list.__str__())
def mutate_copy(l: List[Int]) -> List[Int]:
# def creates an implicit copy of the list because it's mutated
l.append(5)
return l
def main():
var values: List[Int] = [1, 2, 3, 4]
values = mutate_copy(values)
print_list(values)
```
```output
[1, 2, 3, 4, 5]
```
Although the code using `mut` isn't that much shorter, it's more memory
efficient because it doesn't make a copy of the value.
However, remember that the values passed as `mut` must already be mutable.
For example, if you try to take an immutable reference and pass it to another
function as `mut`, you'll get a compiler error because Mojo can't form a
mutable reference from an immutable reference.
:::note
You can't define [default
values](/docs/manual/functions#optional-arguments) for `mut`
arguments.
:::
### Argument exclusivity
Mojo enforces *argument exclusivity* for mutable references. This means that if
a function receives a mutable reference to a value (such as an `mut` argument),
it can't receive any other references to the same value—mutable or immutable.
That is, a mutable reference can't have any other references that *alias* it.
For example, consider the following code example:
```mojo
def append_twice(mut s: String, other: String):
# Mojo knows 's' and 'other' can't be the same string.
s += other
s += other
def invalid_access():
var my_string = "o" # Create a run-time String value
# error: passing `my_string` mut is invalid since it's also passed
# as an immutable reference
append_twice(my_string, my_string)
print(my_string)
```
This code is confusing because the user might expect the output to be `ooo`,
but since the first addition mutates both `s` and `other`, the actual output
would be `oooo`. Enforcing exclusivity of mutable references not only prevents
coding errors, it also allows the Mojo compiler to optimize code in some cases.
One way to avoid this issue when you do need both a mutable and an immutable
reference (or need to pass the same value to two arguments) is to make a copy:
```mojo
def valid_access():
var my_string = "o" # Create a run-time String value
var other_string = my_string # Create a copy of the String value
append_twice(my_string, other_string)
print(my_string)
```
Note that argument exclusivity isn't enforced for register-passable trivial
types (like `Int` and `Bool`) as they're always passed by copy. When
passing the same value into two `Int` arguments, the callee receives two
copies of the value.
## Transfer arguments (`var` and `^`)
If you want your function to take *ownership* of a value, add the `var`
keyword before the argument name.
This convention is often combined with using the postfix `^` transfer sigil
on an argument at the call site.
When using a variable, transferring a value leaves the original variable
uninitialized. You can't use the variable after the transfer until you
assign it a new value of the original type.
### Transferring with `var`
`var` behaves differently depending on whether the caller uses the `^`
transfer sigil and whether the value conforms to `Copyable`.
The `var` keyword doesn't guarantee that the function receives *the original
value*. It guarantees only that the function receives *ownership of a
value*. That happens in one of three ways:
- **Value transfer**: The caller uses the `^` transfer sigil. This
transfers the value, leaving the original variable uninitialized. The function
argument receives ownership.
- **Copying**: Without the transfer sigil, Mojo copies the value. If the
type isn't `Copyable`, this produces a compile-time error.
- **Newly created value**: The caller passes a newly created value,
such as the result of a function call. In this case, no variable owns the
value, so ownership transfers directly to the callee. For example:
```mojo
def take(var s: String):
pass
def main():
take("A brand-new String!")
```
The following code works by making a copy of the string, because `take_text()`
uses the `var` convention, and the caller doesn't include the transfer sigil:
```mojo
def take_text(var text: String):
text += "!"
print(text)
def main():
var message = "Hello" # Create a run-time String value
take_text(message)
print(message)
```
```output
Hello!
Hello
```
However, if you add the `^` transfer sigil when calling `take_text()`, the
compiler complains about `print(message)`, because at that point, the `message`
variable is no longer initialized. That is, this version doesn't compile:
```mojo
def main():
var message = "Hello" # Create a run-time String value
take_text(message^)
print(message) # error: use of uninitialized value 'message'
```
This is a critical feature of Mojo's lifetime checker, because it ensures that
no two variables have ownership of the same value. To fix the error, you must
not use the `message` variable after you end its lifetime with the `^` transfer
sigil. So here is the corrected code:
```mojo
def take_text(var text: String):
text += "!"
print(text)
def main():
var message = "Hello" # Create a run-time String value
take_text(message^)
```
```output
Hello!
```
Regardless of how it receives the value, when the function declares an argument
as `var`, it's certain that it has unique mutable access to that value.
Because the value is owned, the value is destroyed when the function
exits—unless the function transfers the value elsewhere.
For example, in the following example, `add_to_list()` takes a string and
appends it to the list. Ownership of the string is transferred to the list, so
it's not destroyed when the function exits. On the other hand,
`consume_string()` doesn't transfer its `var` value out, so the value is
destroyed at the end of the function.
```mojo
def add_to_list(var name: String, mut list: List[String]):
list.append(name^)
# name is uninitialized, nothing to destroy
def consume_string(var s: String):
print(s)
# s is destroyed here
```
### Transfer implementation details
In Mojo, you shouldn't conflate "ownership transfer" with a "move
operation"—these aren't strictly the same thing.
There are multiple ways that Mojo transfers ownership of a value:
- If a type implements the [move
initializer](/docs/manual/lifecycle/life#move-constructor),
`__init__(take=)`, Mojo may invoke this method *if* a value of that type is
transferred into a function as a `var` argument, *and* the original
variable's lifetime ends at the same point (with or without use of the `^`
transfer sigil).
- In some cases, Mojo optimizes away the move operation entirely, leaving the
value in the same memory location but updating its ownership. In these cases,
a value transfers without invoking either the copy or move
initializers.
In order for the `var` convention to work *without* the transfer sigil, the
value type must be copyable (via `__init__(out self, *, copy: Self)`).
---
## Value semantics
Mojo doesn't enforce value semantics or reference semantics. It supports them
both and allows each type to define how it is created, copied, and moved (if at
all). So, if you're building your own type, you can implement it to support
value semantics, reference semantics, or a bit of both. That said, Mojo is
designed with argument behaviors that default to value semantics, and it
provides tight controls for reference semantics that avoid memory errors.
The controls over reference semantics are provided by the [value ownership
model](/docs/manual/values/ownership), but before we get into the syntax
and rules for that, it's important that you understand the principles of value
semantics. Generally, it means that each variable has unique access to a value,
and any code outside the scope of that variable cannot modify its value.
## Intro to value semantics
In the most basic situation, sharing a value-semantic type means that you create
a copy of the value. This is also known as "pass by value." For example,
consider this code:
```mojo
def main():
var x = 1
var y = x
y += 1
print("x:", x)
print("y:", y)
```
```output
x: 1
y: 2
```
We assigned the value of `x` to `y`, which creates the value for `y` by making a
copy of `x`. When we increment `y`, the value of `x` doesn't change. Each
variable has exclusive ownership of a value.
Whereas, if a type instead uses reference semantics, then `y` would point to
the same value as `x`, and incrementing either one would affect the value for
both. Neither `x` nor `y` would "own" the value, and any variable would be
allowed to reference it and mutate it.
Numeric values in Mojo are value semantic because they're trivial types, which
are cheap to copy.
## Value semantics in Mojo functions
Value semantics also apply to function arguments in Mojo by default. However,
the way in which they apply differs depending on the [argument
convention](/docs/manual/values/ownership#argument-conventions), which is
discussed in the [Ownership](/docs/manual/values/ownership/) page.
For example, in the following function, the `y` argument is immutable by
default, so if the function wants to modify the value in the local scope, it
needs to make a local copy:
```mojo
def add_two(y: Int):
# y += 2 # This would cause a compiler error because `y` is immutable
# We can instead make an explicit copy:
var z = y
z += 2
print("z:", z)
def main():
var x = 1
add_two(x)
print("x:", x)
```
```output
z: 3
x: 1
```
This is all consistent with value semantics because each variable maintains
unique ownership of its value.
The way the function receives the `y` value is a "look but don't touch"
approach to value semantics. This is also a more memory-efficient approach when
dealing with memory-intensive arguments, because Mojo doesn't make any copies
unless we explicitly make the copies ourselves.
Thus, the default behavior for function arguments is fully value
semantic: arguments are immutable references, and any living
variable from the caller is not affected by the function.
But we must also allow reference semantics (mutable references) because it's
how we build performant and memory-efficient programs (making copies of
everything gets really expensive). The challenge is to introduce reference
semantics in a way that does not disturb the predictability and safety of value
semantics.
The way we do that in Mojo is, instead of enforcing that every variable have
"exclusive access" to a value, we ensure that every value has an "exclusive
owner," and destroy each value when the lifetime of its owner ends.
On the next page about [value ownership](/docs/manual/values/ownership/), you'll
learn how to modify the default argument conventions, and safely use reference
semantics so every value has only one owner at a time.
---
## Variables
A variable is a name that holds a value or object. All variables in Mojo
are mutable by default. Their value can change. If you want to define a
constant value that can't change at runtime, see the [`comptime`
keyword](/docs/manual/metaprogramming/comptime-evaluation/#comptime-values)
or pass the value as a non-mutable function argument.
When you declare a variable in Mojo, you allocate a logical storage location,
and bind a name to that storage.
```mojo
var greeting: String = "Hello World"
```
A `var` declaration does three things:
- It declares a logical storage location, which is tied to a particular type.
In this case, it holds `String` instances.
- It binds the name `greeting` to this logical storage location.
- It *initializes* the storage space with a newly created `String` value,
using "Hello World". The new value is *owned by* the variable.
No other variable can own this value unless you transfer its ownership.
## Variable declarations
To declare a variable, use `var` with a name. You can give it a value, a
type annotation, or both. The more you annotate, the more explicit your
code is, and the easier it is to read and maintain:
```mojo
var a = 5 # Mojo infers that a is type Int
var b: Float64 = 3.14 # Explicit declaration of Float64 type
var c: String # The name is created but uninitialized
```
A variable's type never changes. Its storage is strongly typed upon creation
and can only hold values of that type:
```mojo
var count = 8 # count is type Int
count = "Nine?" # Error: can't implicitly convert 'StringLiteral' to 'Int'
```
A variable is scoped to the block in which it is declared. Its value
is destroyed at last use. You may transfer a value from a variable so it no
longer lives in that variable or that scope. The name, that is, the variable
itself, is destroyed when the scope ends.
- Variables are names that hold values.
- Values are data that live in memory.
## Variable scopes
Variables in Mojo use *lexical scoping*. A variable's definition is
determined by where it appears in the source code, not when it executes
at runtime. The specific scope level depends on how the variable is
declared.
Variables have **block-level** scope. Nested code can read and modify
variables defined in an outer scope. An outer scope can't read variables
defined in an inner scope.
For example, the `if` code block shown here creates an inner scope where outer
variables are accessible to read/write, but any new variables do not live
beyond the scope of the `if` block:
```mojo
def lexical_scopes():
var num = 1
var dig = 1
if num == 1:
print("num:", num) # Reads the outer-scope "num"
var num = 2 # Creates new inner-scope "num"
print("num:", num) # Reads the inner-scope "num"
dig = 2 # Updates the outer-scope "dig"
print("num:", num) # Reads the outer-scope "num"
print("dig:", dig) # Reads the outer-scope "dig"
```
```output
num: 1
num: 2
num: 1
dig: 2
```
Note that the `var` statement inside the `if` creates a **new** variable
with the same name as the outer variable. This prevents the inner
if-statement from accessing the outer `num` variable. This is called
"variable shadowing," where the inner scope variable hides or "shadows" a
variable from an outer scope.
The lifetime of the inner `num` ends exactly where the `if` code block ends,
because that's the scope in which the variable was defined.
## Copying and moving values
An assignment statement of a newly created value or a literal establishes
ownership:
```mojo
var owning_variable = "Owned value"
```
An assignment of an existing variable's value transfers ownership of that
value or a copy of that value to the new variable:
```mojo
var source = String("Hello")
var copied = source # A copy
var moved = source^ # A transfer
```
The right-hand side variables must be `Copyable` or `Movable` to be
assigned in this way. After the assignment the new variable owns a value,
whether copied or transferred. A transfer leaves `source` uninitialized, and
you can't use it again until you assign it a new value.
The value on the right-hand side of the assignment statement must be
transferable to the new variable. Here's an example where that doesn't
work:
```mojo
var first: List[Int] = [1, 2, 3]
var second = first # error: 'List[Int]' is not implicitly copyable because
# it doesn't conform to 'ImplicitlyCopyable'
```
The first assignment is no problem: the expression `[1, 2, 3]` creates a
new `List` value without an owner, so `first` becomes that owner without
any ambiguity. The second assignment errors because `first` isn't
implicitly copyable and the value isn't transferred.
Each outcome depends on type features for the values involved in assignment.
- A `Copyable` type can be copied explicitly, by calling its copy
initializer or the `copy()` method.
```mojo
var second = first.copy()
```
Copying leaves `first` unchanged. `second` is assigned its own, uniquely
owned copy of the list.
- `ImplicitlyCopyable` types can be copied without an explicit signal:
```mojo
var one_value = 15
var another_value = one_value # implicit copy
```
Implicitly copyable types are generally simple value types like `Int`,
`Float64`, and `Bool`, which can be copied trivially.
- The ownership of a value can be explicitly transferred from one variable
to another by appending the *transfer sigil* (`^`) after the value to
transfer:
```mojo
var second = first^
```
This moves the value to `second`, and leaves `first` uninitialized.
This ownership may move the value from one memory location to another.
This requires the value to be `Movable`.
## Reference bindings
Some APIs return
[_references_](/docs/manual/values/lifetimes/#working-with-references) to
values owned elsewhere. References avoid copying values. For example, when
you retrieve a value from a collection, the collection returns a reference,
instead of a copy:
```mojo
var animals: List[String] = ["Cats", "Dogs", "Zebras"]
print(animals[2]) # Prints "Zebras", does not copy the value.
```
If you assign a reference to a *variable*, it creates a copy (if the value
is implicitly copyable) or produces an error (if it isn't):
```mojo
var items: List[Int] = [99, 77, 33, 12]
var item = items[1] # item is a copy of items[1]
item += 1 # increments item
print(items[1]) # prints 77
```
To name a reference, use the `ref` keyword to create a reference binding:
```mojo
ref item_ref = items[1] # item_ref is a reference to item[1]
item_ref += 1 # increments items[1]
print(items[1]) # prints 78
```
The name `item_ref` is bound to `items[1]`. All reads and writes to
`item_ref` go to the item it references.
Reference bindings can't be re-assigned:
```mojo
ref item_ref = items[2] # error: invalid redefinition of item_ref
```
For more information on references, see
[Working with references](/docs/manual/values/lifetimes/#working-with-references).
---
## Compilation targets
Mojo compiles code for a range of targets, from your local machine to
other CPUs, operating systems, and GPUs. You can inspect what the
compiler supports, choose a target configuration, and generate code for
that target.
_Compilation targets_ describe where and how your program runs. They define
the platform, CPU, features, and optional accelerators used during code
generation, for both native and cross-compilation workflows, including
GPU-enabled (_heterogeneous builds_).
The Mojo command line compiler lets you inspect your current platform,
select a target configuration, and generate code for that target. Use it
to build for your own system or target other CPUs, operating systems, and
accelerators.
:::caution Work in progress
Cross-compilation support is still in development. You can query
targets, cross-compile to object files and assembly, and target GPU
architectures. Producing a fully linked cross-compiled executable
requires an external linker for the target platform. See
[Emit options](#emit-options) for details on what works today.
:::
## Query your system and available targets
Before setting compilation or cross-compilation flags, check which
targets the compiler supports and what it detects on your system. These
commands list available targets and show how the compiler configures
your current machine.
Use these commands to understand and choose the components of a target,
including the target triple (architecture, vendor, OS), CPU, features,
and accelerators.
:::note
A target triple is a string that identifies the target platform. It lists
an architecture, vendor, and operating system.
:::
### Effective target
The effective target is the configuration the compiler uses for your
current system when you don't set target flags.
Print the full target configuration for your system:
```sh
mojo build --print-effective-target
```
Sample output on an Apple M4 MacBook Pro. The features are truncated
in this example to save space:
```output
Effective target configuration:
--target-triple arm64-apple-darwin25.3.0
--target-cpu apple-m4
--target-features +aes,+bf16,+complxnum,+crc,+dotprod,+fp-armv8,...
--target-accelerator metal:4
```
This output shows the flags that reproduce your host configuration.
Use it to see what the compiler assumes when you don't set target flags.
### Supported targets
List the target architectures the compiler can generate code for. Use
this command to see which architectures are available before selecting a
target or composing a target triple.
```sh
mojo build --print-supported-targets
```
For example:
```output
Registered Targets:
arm64 - ARM64 (little endian)
arm64_32 - ARM64 (little endian ILP32)
aarch64 - AArch64 (little endian)
aarch64_32 - AArch64 (little endian ILP32)
aarch64_be - AArch64 (big endian)
r600 - AMD GPUs HD2XXX-HD6XXX
amdgcn - AMD GCN GPUs
hexagon - Hexagon
...
```
### Supported target CPUs
List valid CPU names for a given target triple. Set `--target-triple`
to select the target triple and narrow the results.
```sh
mojo build --print-supported-cpus \
--target-triple=aarch64-apple-macosx
```
For example:
```output
Available CPUs for target aarch64-apple-macosx:
a64fx
ampere1
apple-a10
apple-a11
apple-m1
apple-m4
...
```
### Supported accelerators
List the accelerator architectures the compiler can target.
```sh
mojo build --print-supported-accelerators
```
```output
Supported Accelerator Architectures:
NVIDIA (CUDA):
sm_52 - Maxwell (GTX 970)
sm_60 - Pascal (Tesla P100)
sm_90 - Hopper (H100)
...
AMD (ROCm/HIP):
gfx942 - CDNA3 (MI300X)
mi300x - (alias) -> gfx942
...
Apple Silicon GPU:
apple-m1 - Apple M1
apple-m2 - Apple M2
...
Other:
cuda - Generic CUDA
```
## How Mojo describes a build target
When the compiler generates machine code, it needs a few key details
about the hardware it targets:
- The **architecture** defines the base instruction set, such as x86-64
or AArch64.
- The **CPU model** adds processor-specific behavior and may enable
instructions beyond the base.
- The **feature set** controls individual hardware capabilities that
can be enabled or disabled, such as AVX-512 or Neon.
For accelerator targets, one more detail applies:
- The **accelerator architecture** identifies the GPU or other
accelerator to generate device code for.
If you don't set these explicitly, the compiler uses your host system.
### Target triples
A target triple identifies the platform you're compiling for. It lists the
architecture, vendor, and operating system in a single value:
```text
x86_64-unknown-linux-gnu
aarch64-apple-macosx
```
The triple sets the overall execution environment and binary
conventions. It's the starting point for cross-compilation and works
with both flag sets described in the next section.
## Two ways to set your target
Mojo provides two sets of flags to specify target hardware. They reach
the same result through different interfaces, and you can't mix them in
one command. These are Mojo target flags and GCC/Clang-compatible flags.
### Mojo target flags
These flags let you set the triple, CPU, and features directly.
| Flag | Purpose |
|------------------------|---------------------------------|
| `--target-triple` | Platform (arch + vendor + OS) |
| `--target-cpu` | Specific processor model |
| `--target-features` | Individual feature toggles |
| `--target-accelerator` | GPU or accelerator architecture |
:::note
When cross-compiling with Mojo target flags, set `--target-cpu` with
`--target-triple`. The CPU defaults to your host processor, which may not
be valid for the target architecture. Omitting `--target-cpu` when
cross-compiling to a different architecture produces an error such as
`failed to create target info: unknown target CPU 'apple-m4'`.
:::
For example:
```sh
mojo build --target-triple aarch64-unknown-linux-gnu \
--target-cpu cortex-a72 \
--emit object -o myapp.o myapp.mojo
```
Use `--target-features` to enable or disable individual hardware
extensions.
```sh
mojo build --target-triple x86_64-unknown-linux-gnu \
--target-cpu x86-64-v3 \
--target-features "+avx512f" \
--emit object -o myapp.o myapp.mojo
```
### GCC/Clang-compatible flags
Mojo supports the same `--march`, `--mcpu`, and `--mtune` flags used
in GCC and Clang. These flags follow the behavior documented in the
GCC manual and work as they do in `clang`.
| Flag | Purpose |
|-----------|--------------------------------------------------|
| `--march` | Architecture or CPU subtype to generate code for |
| `--mcpu` | CPU model (sets architecture and tuning) |
| `--mtune` | Optimization hint for a specific processor |
For example:
```sh
mojo build --target-triple x86_64-unknown-linux-gnu \
--mcpu=haswell \
--emit object -o myapp.o myapp.mojo
```
**`--march`** controls which instructions the compiler can use. Code
compiled with `--march=skylake-avx512` can use AVX-512 instructions,
but it won't run on hardware that lacks them.
**`--mcpu`** sets both the architecture and tuning from a single CPU
name.
**`--mtune`** guides optimization without changing which instructions
the compiler uses. It tells the compiler to prefer instruction
sequences that run faster on the given processor. The code still runs
correctly on other processors with the same instruction support.
:::note Known issue
When using `--mcpu` or `--march` to cross-compile from a host with a
different architecture, the compiler may print warnings about unrecognized
features. These warnings are harmless — the compiler ignores the unsupported
features and the output is correct. This will be fixed in a future release.
:::
The `--march` flag supports extension syntax for adding features
inline:
```sh
mojo build --target-triple x86_64-unknown-linux-gnu \
--march=x86-64-v3+avx512f \
--emit asm -o myapp.s myapp.mojo
```
:::caution Architecture-specific behavior
The exact relationship between `--march` and `--mcpu` varies by target
architecture, matching GCC/Clang conventions:
- **x86**: `--march` or `--mcpu` specifies a CPU subtype like
`skylake-avx512`. With `--mcpu=generic`, `--march` is treated as an
architecture baseline.
- **AArch64**: `--march` sets the base architecture (like `armv8.2-a`),
`--mcpu` sets the specific CPU (like `neoverse-n1`). If you only set
the architecture, the CPU defaults to `generic`.
- **ARM**: `--march` sets the base architecture, `--mcpu` sets the
specific CPU. If you only set the architecture, the default CPU for
that architecture is used.
:::
### ⚠️ Don't mix the two families {#dont-mix-the-two-families}
The Mojo compiler enforces a clear separation between these flag
families. Using `--target-cpu` or `--target-features` with `--march`
or `--mcpu` in the same command produces an error:
```sh
# This fails:
mojo build --target-cpu=haswell --mcpu=skylake myapp.mojo
```
Error:
```output
error: --target-cpu cannot be used with --march or --mcpu;
use either --target-cpu/--target-features or --march/--mcpu/--mtune
```
Pick one family and use it consistently. Both produce the same result
for the same hardware.
### Shared flags
Two flags work with both families:
- `--target-triple` is always valid and is typically required for
cross-compilation, regardless of which family you use.
- `--target-accelerator` is always valid and is used to target GPUs
with either family.
## Accelerator targets
Mojo supports _heterogeneous builds_ that generate host code for the
CPU and device code for a GPU in a single build. Use
`--target-accelerator` to specify the GPU architecture:
```sh
mojo build --target-accelerator=sm_90 myapp.mojo
```
For NVIDIA and AMD targets, use a prefix to select the platform:
```sh
mojo build --target-accelerator=nvidia:sm_90 myapp.mojo # NVIDIA H100
mojo build --target-accelerator=amdgpu:gfx942 myapp.mojo # AMD MI300X
```
When you use `--emit asm` with a GPU target, the compiler produces a
separate file for each kernel alongside the host assembly: `.ptx` for
NVIDIA, `.amdgcn` for AMD, and `.ll` for Metal.
## Cross-compilation in practice
### Generate an object file for another platform
```sh
mojo build --target-triple aarch64-unknown-linux-gnu \
--target-cpu cortex-a72 \
--emit object -o myapp.o myapp.mojo
```
This produces an object file for the target platform. Link it with a
toolchain for that platform.
### Generate assembly for inspection or external toolchains
```sh
mojo build --target-triple x86_64-unknown-linux-gnu \
--emit asm -o myapp.s myapp.mojo
```
This produces assembly for the target platform. Use it for inspection
or pass it to an external toolchain for further processing.
### Target a specific CPU with tuning
```sh
mojo build --target-triple x86_64-unknown-linux-gnu \
--march=x86-64 --mcpu=haswell --mtune=skylake \
--emit object -o myapp.o myapp.mojo
```
This generates code for the Haswell instruction set and optimizes it
for Skylake.
### GPU kernel compilation
```sh
mojo build --target-accelerator=nvidia:sm_90 myapp.mojo
```
This compiles GPU kernels for the specified accelerator and includes
them with the host build.
:::caution Runtime dependencies
Cross-compiled binaries don't include external libraries. This includes
Python libraries, C libraries, and Modular runtime libraries. The target
environment must provide all runtime dependencies your program needs.
:::
## Emit options
The `--emit` flag controls the output `mojo build` produces. These
options are essential for cross-compilation because you can't yet
produce linked executables with the Mojo compiler.
| Value | Output | Status |
|-----------------|-----------------------------|--------|
| `exe` (default) | Executable binary | Native |
| `shared-lib` | Shared (dynamic) library | Native |
| `object` | Object file (experimental) | Both |
| `llvm` | Unoptimized LLVM IR | Both |
| `llvm-bitcode` | Unoptimized LLVM IR bitcode | Both |
| `asm` | Assembly (+ GPU sidecars) | Both |
### What's working
Outputs that don't require linking work with any supported target:
- `--emit object` — produces a relocatable object file for the target
- `--emit asm` — produces assembly for the target
- `--emit llvm` — produces LLVM IR configured for the target
- `--emit llvm-bitcode` — produces LLVM bitcode for the target
Outputs that require linking need a linker for the target platform,
which Mojo doesn't provide and aren't working:
- `--emit exe` — fails at the link step when cross-compiling
- `--emit shared-lib` — fails at the link step when cross-compiling
To produce a cross-compiled executable or shared library, generate an
object file and link it with a toolchain for your target platform.
## Call a Mojo shared library from C or C++
You can compile Mojo code into a shared library and call it from a
program written in another language, such as C or C++, through the C
ABI.
### Build and export
Build the shared library with `--emit shared-lib`:
```sh
mojo build mylib.mojo --emit shared-lib -o libmylib.so
```
(Use a `.dylib` extension on macOS.)
Mark each function you want to call from the host with the
[`@export`](/docs/reference/decorators/export/) decorator, giving it a
name that's a valid C identifier and the `abi("C")` effect so it
follows the C calling convention.
### Initialize the Mojo runtime
When a Mojo program starts from its own `main()` function, compiler-generated
startup code initializes the Mojo runtime — the thread pool that parallel APIs
such as
[`parallelize()`](https://max.modular.com/api/mojo/max/algorithm/backend/cpu/parallelize/parallelize/)
depend on. When the process `main()` belongs to a C or C++ host instead, that
startup code never runs, so the runtime is never initialized. An exported
function that then uses a runtime-dependent API crashes with a segmentation
fault.
To fix this, call
[`initialize_runtime()`](/docs/std/runtime/initialize_runtime/)
before any runtime-dependent Mojo code executes. The call is idempotent
and inexpensive when the runtime is already initialized, and one
initialization covers all threads in the process. There are two common
patterns:
- Call `initialize_runtime()` at the start of every exported function.
This is the simplest approach and imposes no calling contract on the
host program.
- Export a dedicated initialization function and require the host to
call it once before anything else:
```mojo
from std.runtime import initialize_runtime
@export("mylib_init")
def mylib_init() abi("C"):
initialize_runtime()
```
### Complete example
The following Mojo library exports one function that fills a list in
parallel and returns a checksum:
```mojo title="mylib.mojo"
from max.algorithm import parallelize
from std.runtime import initialize_runtime
@export("parallel_sum")
def parallel_sum(n: Int64) abi("C") -> Int64:
initialize_runtime()
var count = Int(n)
var results = List[Int64](length=count, fill=0)
def fill(i: Int) {mut results}:
results[i] = Int64(i)
parallelize(fill, count)
var total = Int64(0)
for r in results:
total += r
return total
```
A C host program that calls it:
```c title="main.c"
#include
extern long long parallel_sum(long long n);
int main(void) {
printf("sum=%lld\n", parallel_sum(1000));
return 0;
}
```
Build and run on Linux:
```sh
mojo build mylib.mojo --emit shared-lib -o libmylib.so
cc main.c -o main -L. -lmylib -Wl,-rpath,'$ORIGIN'
./main
```
On macOS:
```sh
mojo build mylib.mojo --emit shared-lib -o libmylib.dylib
install_name_tool -id @rpath/libmylib.dylib libmylib.dylib
cc main.c -o main -L. -lmylib -Wl,-rpath,@loader_path
./main
```
Both print:
```output
sum=499500
```
:::note Differences from a Mojo executable
Without a Mojo `main()` function, some process-level setup that Mojo
executables perform doesn't happen:
- `sys.argv()` isn't populated with the host program's arguments.
- The signal handler that prints a stack trace on a crash isn't
installed.
- The runtime, once initialized, remains alive until the process
exits; there is no API to shut it down.
- The host program's dynamic loader must be able to locate the Modular
runtime libraries that the shared library depends on (for example,
through the rpath entries embedded in the shared library).
:::
---
## Debugging
The Mojo extension for Visual Studio Code enables you to use VS Code's built-in
debugger with Mojo code. This page describes the features available through the
VS Code Mojo extension, as well as current limitations of the Mojo debugger.
You can install the Mojo extension from either the
[Visual Studio Code Marketplace](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo)
or the
[Open VSX Registry](https://open-vsx.org/extension/modular-mojotools/vscode-mojo).
To use the Mojo extension, you must also
[install the `mojo` package](/install/)—or, if you're developing for
the MAX framework,
[install the `modular` package](https://max.modular.com/packages/), which
includes the `mojo` package.
:::note
The Mojo extension relies on the Python extension for locating your Python
environment. In some cases, this appears to default to your globally-installed
environment, even when a virtual environment exists. If the Mojo extension
cannot find your SDK installation, try invoking the "Python: Set Project
Environment" command and selecting your virtual environment.
:::
For complete coverage of VS Code's debugging features, see
[Debugging in Visual Studio Code](https://code.visualstudio.com/docs/editor/debugging).
The `mojo` package includes the [LLDB debugger](https://lldb.llvm.org/) and a
Mojo LLDB plugin. Together these provide the low-level debugging interface for
the Mojo extension. You can also use the `mojo debug` command to start a
command-line debugging session using LLDB or to launch a Mojo debugging session
in VS Code.
The `mojo` package also includes support for debugging Mojo programs running on
GPU. This requires some extra software and configuration. Currently GPU
debugging only works with NVIDIA GPUs. For details, see
[GPU debugging](https://max.modular.com/gpu/debugging/).
## Start debugging
There are several ways to start a debug session in VS Code.
To start debugging, you'll need to have a Mojo project to debug. There are a
number of examples ranging from simple to complex in [our GitHub
repo](https://github.com/modular/modular/tree/mojo/v1.1.0/Mojo/examples).
:::note VS Code veteran?
If you're already familiar with debugging in VS Code, the
material in this section will mostly be review. You might want to skip ahead to
[Launch configurations](#launch-configurations)
or see [Using the debugger](#using-the-debugger) for notes on the features
supported in the Mojo debugger.
:::
### Quick run or debug
If your active editor tab contains a Mojo file with an `def main()` entry point,
one of the quickest ways to run or debug it is using the **Run or Debug** button
in the Editor toolbar.

To start debugging the current file:
- Open the **Run or Debug** dropdown menu and choose **Debug Mojo File** or
**Debug Mojo File in Dedicated Terminal**.

The two debug configurations differ in how they handle input and output:
- **Debug Mojo File** launches the Mojo program detached from any terminal.
Standard output and standard error output for the program are displayed in the
**Debug Console**. You can't write to the program's standard input, but you
can see the program's output and interact with the debugger in a single
location.
- **Debug Mojo File in Dedicated Terminal** creates a new instance of VS Code's
integrated terminal and attaches the program's input and output to the
terminal. This lets you interact with the program's standard input, standard
output and standard error output in the terminal, while the **Debug Console**
is used only for interactions with the debugger.
The **Run or Debug** button uses predefined launch configurations. There's
currently no way to modify the `args`, `env`, `cwd` or other settings for
programs launched with the **Run or Debug** configurations. If you need to
customize any of these things, see [Edit launch
configurations](#edit-launch-configurations).
After you choose one of the debug configurations, the button updates to show
the debug symbol. Click the button to re-run the previous configuration.
.
### Run and Debug view
The **Run and Debug** view includes a button to launch debug sessions and a
menu to select debug configurations. It also has areas to display current
variables, watch expressions, the current call stack, and breakpoints.

Figure 1. Run and Debug view
To open **Run and Debug** view, click the **Run and Debug** icon in the
**Activity Bar** (on the left side of the VS Code window) or press
Control+Shift+D (Command+Shift+D on macOS).

If you haven't created any launch configurations in the current project,
VS Code shows the **Run start view**.

Figure 2. Run start view
If you've already launched a debug session or created a `launch.json` file to
define launch configurations, you'll see the **Launch configurations** menu,
which lets you choose configurations and start debug sessions:

Figure 3. Launch configurations menu
### Other ways to start a debug session
There are a number of other ways to start a debug session.
#### Launching from the Command Palette
If you have a Mojo file open in your active editor, you can also start a debug
session from the **Command Palette**.
1. Click **View** > **Command Palette** or press Control+Shift+P
(Command+Shift+P on macOS).
2. Enter "Mojo" at the prompt to bring up the Mojo commands. You should see the
same debug configurations described in [Quick run or
debug](#quick-run-or-debug).
#### Launch from the File Explorer
To launch a debug session from the **File Explorer** view:
1. Right-click on a Mojo file.
2. Select a Mojo debug configuration.
You should see the same debug configurations described in [Quick run or
debug](#quick-run-or-debug).
#### Debug with F5
Press F5 to start a debug session using the current debug configuration.
If you don't have any existing debug configurations available to select, and
your active editor contains a Mojo file with an `def main()` entry point,
pressing F5 will launch and debug the current file using the **Debug Mojo
File** action described in [Quick run or debug](#quick-run-or-debug).
## Starting the debugger from the command line
Use the `mojo debug` command to start a debug session from the command line. You
can choose from two debugging interfaces:
- With the `--vscode` flag, `mojo debug` starts a debug session on VS Code if
it's running and the Mojo extension is enabled.
- Without the `--vscode` flag, `mojo debug` starts a command-line [LLDB
debugger](https://lldb.llvm.org/) session.
You can choose to build and debug a Mojo file, run and debug a compiled binary,
or to attach the debugger to a running process.
:::note Environment variables
When you debug a program from the command line using `--vscode`, the program
runs with the environment variables set in the terminal. When launching from
inside VS Code via the GUI, the environment is defined by the VS Code
[launch configuration](#launch-configurations).
:::
For a full list of command-line options, see the [`mojo debug` reference
page](/docs/cli/debug).
### Start a debug session from the command line
With VS Code open, run the following command (either from VS Code's integrated
terminal or an external shell):
```bash
mojo debug --vscode myproject.mojo
```
Or to debug a compiled binary:
```bash
mojo debug --vscode myproject
```
For best results, build with the `-O0 -g` command-line options when you build a
binary that you intend to debug—this produces a binary with full debug info.
(When you call `mojo debug` on a Mojo source file, it includes debug
information by default.) See the [`mojo build` reference page](/docs/cli/build/)
for details on compilation options.
### Attach the debugger to a running process from the command line
You can also attach the debugger to a running process by specifying either the
process ID or process name on the command line:
```bash
mojo debug --vscode --pid
```
Or:
```bash
mojo debug --vscode --process-name
```
## Launch configurations
VS Code *launch configurations* let you define setup information for debugging
your applications.
The Mojo debugger provides the following launch configuration templates:
- Debug current Mojo file. Launches and debugs the Mojo file in the active
editor tab. Effectively the same as the **Debug Mojo File** action described
in [Quick run or debug](#quick-run-or-debug), but with more configuration
options.
- Debug Mojo file. Like the previous entry, except that it identifies a
specific file to launch and debug, no matter what file is displayed in the
active editor.
- Debug binary. This configuration operates on a prebuilt binary, which could
be written in any mixture of languages supported by LLDB (Mojo, C, C++, etc.).
You need to set the `program` field to the path of your binary.
- Attach to process. Launches a debug session attached to a running process. On
launch, you choose the process you want to debug from a list of running
processes.
You can edit any of these templates to customize them. All VS Code launch
configurations must contain the following attributes:
- `name`. The name of the launch configuration, which shows up in the UI (for
example, "Run current Mojo file").
- `request`. Can be either `launch` (to run a program from VS Code) or `attach`
(to attach to and debug a running file).
- `type`. Use `mojo-lldb` for the Mojo debugger. Use `mojo-cuda-gdb` to debug on
GPU.
In addition, Mojo launch configurations can contain the following attributes:
- `args`. Any command-line arguments to be passed to the program.
- `cwd`. The current working directory to run the program in.
- `description`. A longer description of the configuration, not shown in the UI.
- `env`. Environment variables to be set before running the program.
- `mojoFile`. Path to a Mojo file to launch and debug.
- `pid`. Process ID of the running process to attach to.
- `program`. Path to a compiled binary to launch and debug, or the
program to attach to.
- `runInTerminal`. True to run the program with a dedicated terminal, which
allows the program to receive standard input from the terminal. False to run
the program with its output directed to the **Debug Console**.
Mojo GPU launch configurations can contain the following attributes:
- `breakOnLaunch`. Set to true to automatically break when a GPU kernel
launches.
- `initCommands`. An array of commands to issue to the debugger on startup. To
use the classic CUDA-GDB debugger backend, add the following lines to your
configuration:
```json
"initCommands": [
"set environment CUDBG_USE_LEGACY_DEBUGGER=1"
],
```
- `legacyDebugger`. Set to true to use the classic debugger backend.
If configuration is a `launch` request, the configuration must include either
the `mojoFile` or `program` attribute.
For `attach` requests, the configuration must include either the `pid` or
`program` attribute.
VS Code performs variable substitution on the launch configurations. You can
use `${workspaceFolder}` to substitute the path to the current workspace, and
`${file}` to represent the file in the active editor tab. For a complete list
of variables, see the VS Code [Variables
reference](https://code.visualstudio.com/docs/editor/variables-reference).
For more information, see the VS Code documentation for
[Launch configurations](https://code.visualstudio.com/docs/editor/debugging#_launch-configurations).
:::note Compilation options
Mojo launch configurations don't allow you to specify compilation options. If
you need to specify compilation options, you can build the binary using [`mojo
build`](/docs/cli/build), then use a launch configuration with the `program`
option to launch the compiled binary. Or if you [start the debugger from the
command line](#starting-the-debugger-from-the-command-line), you can pass
compilation options to the `mojo debug` command.
:::
### Edit launch configurations
To edit launch configurations:
1. If the **Run and Debug** view isn't already open, click the **Run and
Debug** icon in the **Activity Bar** (on the left side of the VS Code window)
or press Control+Shift+D (Command+Shift+D on macOS).

2. Create or open the `launch.json` file:
1. If you see the **Run start view**, click **create a launch.json file**.
2. If you already have launch configurations set up, click the gear icon
next to the **Launch configurations** menu.

3. Select **Mojo** from the list of debuggers.
VS Code opens the new `launch.json` file in an editor tab, with templates for
some common debug actions. Click **Add configuration** to add a new
configuration template.
## Using the debugger
When a debug session is running, use the debug toolbar to pause, continue, and
step through the program.

The buttons on the toolbar are:
- **Continue/Pause**: If the program is stopped, resume the normal execution of
the program up to the next breakpoint, signal or crash. Otherwise, pause all
the threads of the program at once.
- **Step Over**: Execute the next line of code without stopping at function
calls.
- **Step Into**: Execute the next line of code and stop at the first function
call. If the program is stopped just before a function call, steps into the
function so you can step through it line-by-line.
- **Step Out**: Finish the execution of the current function and stop right
after returning to the parent function.
- **Restart**: If this is a `launch` session, terminate the current program and
restart the debug session. Otherwise, detach from the target process and
reattach to it.
- **Stop**: If this is a `launch` session, terminate the current program.
Otherwise, detach from the target process without killing it.
The debugger currently has the following limitations:
- No support for breaking automatically on Mojo errors.
- When stepping out of a function, the returned value is not displayed.
- LLDB doesn't support stopping or resuming individual threads.
### Breakpoints
The Mojo debugger supports setting
[standard breakpoints](https://code.visualstudio.com/docs/editor/debugging#_breakpoints),
[logpoints](https://code.visualstudio.com/docs/editor/debugging#_logpoints),
[function breakpoints](https://code.visualstudio.com/docs/editor/debugging#_function-breakpoints),
[data breakpoints](https://code.visualstudio.com/docs/editor/debugging#_data-breakpoints),
and
[triggered breakpoints](https://code.visualstudio.com/docs/editor/debugging#_triggered-breakpoints),
as described in the VS Code documentation. The Mojo debugger also supports
*error breakpoints* (also known as "break on raise"), which break whenever a
`raise` statement is executed.
When debugging Mojo code, the debugger doesn't support conditional breakpoints
based on an expression (it does
support hit counts, which VS Code classifies as a kind of conditional
breakpoint).
When editing a breakpoint, you're offered four options:
- **Expression**. Set a conditional breakpoint (not currently supported).
- **Hit Count**. Add a hit count to a breakpoint (supported).
- **Log Message**. Add a logpoint (supported)
- **Wait for Breakpoint**. Add a triggered breakpoint (supported).
#### Set a hit count breakpoint
A hit count breakpoint is a breakpoint that only breaks execution after the
debugger hits it a specified number of times.
To add a hit count breakpoint:
1. Right click in the left gutter of the editor where you want to place the
breakpoint, and select **Add Conditional Breakpoint.**
2. Select **Hit Count** from the menu and enter the desired hit count.
To change an existing breakpoint to a hit count breakpoint:
1. Right click on the breakpoint in the left gutter of the editor and select
**Edit breakpoint**.
2. Select **Hit Count** from the menu and enter the desired hit count.
You can also edit a breakpoint from the **Breakpoints** section of the **Run and
Debug** view:
- Right-click on the breakpoint and select **Edit Condition**, or,
- Click the **Edit Condition** icon next to the breakpoint.
This brings up the same menu, **next to the breakpoint in the editor tab**.
#### Enable error breakpoints
You can enable and disable error breakpoints in VS Code by selecting "Mojo
Raise" in the **Breakpoints** section of the **Run and Debug** view. If enabled
during debugging, executing a `raise` statement causes the debugger to stop
execution and highlight the line of code where the error was raised.

### View local variables
When a program is paused in the debugger, the editor shows local variable values
inline. You can also find them in the **Variables** section of the **Run and
Debug** view.

Figure 4. Local variable values displayed in the debugger
### View the call stack
When a program is paused in the debugger, the **Run and Debug** view shows the
current call stack. (You may see multiple call stacks, one for each active
thread in the program.)

Figure 5. Call stack in Run and Debug view
The **Call Stack** section of the Run and Debug view shows a stack frame for
each function call in the current call stack. Clicking on the name of the
function highlights the current line in that function. For example, in Figure
5, the program is paused at a breakpoint in `nested2()`, but the parent
function, `nested1()` is selected in the call stack. The editor highlights the
current line in `nested1()` (that is, the call to `nested2()`) and shows the
current local variable values for `nested1()`.
### Use the Debug Console
The **Debug Console** gives you a command-line interface to the debugger. The
**Debug Console** processes LLDB commands and Mojo expressions.
Anything prefixed with a colon (`:`) is treated as an LLDB command. Any other
input is treated as an expression.
Currently Mojo expressions are limited to inspecting variables and their fields.
The console also supports subscript notation (`vector[index]`) for certain data
structures in the standard library, including `List` and `SIMD`.
In the future, we intend to provide a way for arbitrary data structures to
support subscript notation in the **Debug Console**.
:::note
The **Debug Console** only accepts input when the program is paused.
:::
## Tips and tricks
There are several features in the standard library that aren't directly related
to the debugger, but which can help you debug your programs. These include:
- Programmatic breakpoints.
- Setting parameters from the Mojo command line.
### Set a programmatic breakpoint
To break at a specific point in your code, you can use the built-in
[`breakpoint()`](/docs/std/builtin/breakpoint/breakpoint/) function:
```mojo
if some_value.is_valid():
do_the_right_thing()
else:
# We should never get here!
breakpoint()
```
If you have VS Code open and run this code in debug mode (either using VS Code
or `mojo debug`), hitting the `breakpoint()` call causes an error, which
triggers the debugger.
:::note Assertions
The [`testing`](/docs/std/testing/testing/) module includes a number of
ways to specify assertions. Assertions also trigger an error, so can open the
debugger in the same way that a `breakpoint()` call will.
:::
### Set parameters from the Mojo command line
You can use the [`sys`](/docs/std/sys/) module to retrieve
parameter values specified on the Mojo command line. Among other things, this
is an easy way to switch debugging logic on and off. For example:
```mojo
from std.sys import is_defined
def some_function_with_issues():
# ...
comptime if is_defined["DEBUG_ME"]():
breakpoint()
```
To activate this code, use the [`-D` command-line
option](/docs/cli/debug#compilation-options) to define `DEBUG_ME`:
```bash
mojo debug -D DEBUG_ME main.mojo
```
The `is_defined()` function returns a compile-time true or false value based on
whether the specified name is defined. Since the `breakpoint()` call is inside a
[`comptime if` statement](/docs/manual/metaprogramming/comptime-evaluation/#comptime-if),
it is only included in the compiled code when the `DEBUG_ME` name is defined on
the command line.
## Troubleshooting
### `error: can't connect to the RPC debug server socket`
If using `mojo debug --vscode` gives you the message `error: can't connect to
the RPC debug server socket: Connection refused`, try the following possible
fixes:
- Make sure VS Code is open.
- If VS Code is already open, try restarting VS Code.
- If there are other VS Code windows open, try closing them and then restarting.
This error can sometimes occur when multiple windows have opened and closed in
certain orders.
### `error: couldn't get a valid response from the RPC server`
If using `mojo debug --vscode` gives you the message `error: couldn't get a
valid response from the RPC server`, try the following possible fixes:
- Make sure VS Code is open to a valid Mojo codebase. This error can sometimes
happen if the VS Code window is open to some other codebase.
- If there are multiple VS Code windows open, try closing all but the one you
wish to debug in.
- Restart VS Code.
- Reinstall the SDK and restart VSCode.
- If you are working on a development version of the SDK, make sure that all
SDK tools are properly built with your build system, and then reload VS Code.
- As a last resort, restarting your entire computer can fix this problem.
If these steps don't help, please file an issue. We'd love your help identifying
possible causes and fixes!
---
## Mojo compilation feature toggles
Mojo provides several mechanisms for compile-time feature gating and
configuration:
- **Compile-time defines** (`-D` and `sys.defines`): pass values from
the command line into Mojo code
- **Compile-time conditionals and platform detection** (`comptime if`,
`comptime assert`, `sys.info`): branch or halt compilation based on
compile-time conditions
- **Debug and optimization gating** (`debug_assert()`): control debug-only
behavior and runtime checks
## Compile-time conditionals
### Using `comptime assert` to establish preconditions
`comptime assert` halts compilation when its condition evaluates to
`False`. Unlike a runtime assertion, it executes during compilation and
produces a compiler error with your message.
Use `comptime assert` to declare compile-time preconditions on parameters
or compilation targets. For example, say you call a GPU-specific function
from a CPU build:
```mojo
from std.sys import is_gpu
def gpu_kernel():
# Called from a CPU build
comptime assert is_gpu(), "this function requires a GPU target"
# ... GPU-specific code
```
When you call `gpu_kernel()`, the compiler prints a `constraint failed:`
note with your message, pointing at the assert:
```text
note: constraint failed: this function requires a GPU target
comptime assert is_gpu(), "this function requires a GPU target"
^
```
### Feature gating with `comptime if`
Use `comptime if` to select code paths at compile time. The condition
must be *parameter-evaluable*, that is, the compiler must reason about
it and it can depend on `comptime` values and parameter expressions:
Call:
```sh
mojo run -Dmode=release hello.mojo
```
Code:
```mojo
from std.sys import get_defined_string
def main():
comptime mode = get_defined_string["mode", "debug"]()
comptime if mode == "release":
print("optimized path")
else:
print("debug path with extra checks")
```
## Compile-time defines
The `-D` flag passes key-value pairs from the command line into Mojo code.
The `std.sys.defines` module reads them.
Call:
```sh
mojo run -Dmode=release -Dverbose -Dmax_threads=8 hello.mojo
# or
mojo run -D mode=release -D verbose -D max_threads=8 hello.mojo
```
You can write either `-Dkey=value` or `-D key=value`. Keys and values
must be joined with `=`.
Supported forms include:
- `-D KEY=VALUE`: the value is parsed as a string, integer, or boolean,
depending on which `get_defined_*[]()` function reads it
- `-D KEY`: defines a flag with no value. `is_defined[]()` returns
`True`. Use `is_defined[]()` for presence checks
- `-D KEY=42`: numeric values can be read with
`get_defined_int[]()`
The `sys.defines` module exposes several functions for reading
compile-time defines. All define names are compile-time
`StaticString` parameters, not runtime strings.
### `is_defined[name]()`
`is_defined[name]()` returns `True` when `-D name` was passed,
regardless of its value. It never errors.
Call:
```sh
mojo -Dverbose hello.mojo
```
Code:
```mojo
from std.sys import is_defined
def main():
comptime if is_defined["verbose"]():
print("verbose mode enabled")
```
`is_defined[name]()` is similar to C's `#ifdef`. It checks only whether
a define exists. The value is ignored.
Use it when any value enables the feature, or when you only care that
the flag was passed. Use other `get_defined_*[]()` functions to read
the value itself.
### `get_defined_bool[name, default=False]()`
`get_defined_bool[name, default=False]()` returns a `Bool`. It
distinguishes between "defined" and "truthy".
The following values are treated as `True`:
- `1`
- `true`, `True`, `TRUE`
- `on`, `On`, `ON`
Any other assigned string value returns `False`.
This function errors when the define does not provide a value.
| Command | Compiler view | Result |
|--------------------------------|-----------------------------|--------------------------------------------------|
| `mojo -D verbose app.mojo` | `verbose` defined, no value | Error |
| `mojo -D verbose=on app.mojo` | `verbose="on"` | `True` |
| `mojo -D verbose=yes app.mojo` | `verbose="yes"` | `False` (`yes` is not a recognized truthy value) |
| `mojo app.mojo` | define missing | `default` → `False` |
```mojo
from std.sys import get_defined_bool
def main():
comptime verbose = get_defined_bool["verbose"]()
comptime if verbose:
print("verbose mode enabled")
```
Avoid `default=True`. It reverses the meaning in a confusing way:
missing values become `True`, while present-but-non-truthy values such
as `-D verbose=banana` or `-D verbose=0` become `False`.
If you need `default=True`, consider using `is_defined[]()` instead. It
expresses intent more clearly.
### `get_defined_int[name]()` and `get_defined_int[name, default]()`
`get_defined_int[name]()` returns an `Int`. If the define is missing or
the value is not a valid integer, compilation fails.
Use this only when the define is required.
Call:
```sh
mojo -D max_threads=8 app.mojo
```
Code:
```mojo
from std.sys import get_defined_int
def main():
comptime threads = get_defined_int["max_threads"]()
print(t"Up to {threads} threads")
```
The parser accepts only base-10 integers. For example, `-D N=10`
works. `-D N=0x10`, `-D N=0o10`, and `-D N=1_000` all fail at the
`get_defined_int[]()` call site. The values are stored as
strings and Mojo doesn't recognize those formats as integers.
Non-integer values such as `-D max_threads=eight` fail the same way.
If you encounter these errors, check command-line spelling and format.
The defaulted version returns the provided value instead of erroring
when the define is missing.
Call:
```sh
mojo -D max_threads=8 app.mojo
```
Code:
```mojo
from std.sys import get_defined_int
def main():
comptime threads = get_defined_int["max_threads", 4]()
print("using", threads, "threads")
```
The default handles only missing defines. If the define exists but its
value is not a valid integer, compilation still fails.
### `get_defined_string[name]()` and `get_defined_string[name, default]()`
`get_defined_string[name]()` returns a `StaticString`. Compilation fails
if the define is missing.
Use this when the define is required.
Call:
```sh
mojo -D mode=release app.mojo
```
Code:
```mojo
from std.sys import get_defined_string
def main():
comptime mode = get_defined_string["mode"]()
comptime if mode == "release":
print("release build")
```
The defaulted version returns `default` instead of erroring when the define
is entirely missing.
Call:
```sh
mojo -D mode=release app.mojo # release
# or
mojo app.mojo # debug (default)
```
Code:
```mojo
from std.sys import get_defined_string
def main():
comptime mode = get_defined_string["mode", "debug"]()
comptime if mode == "release":
print("release build")
```
### `get_defined_dtype[name, default]()`
`get_defined_dtype[name, default]()` returns a `DType`. A default value
is required.
Use this to parameterize numeric code with a user-selected type.
Call:
```sh
mojo -D dtype=float8_e4m3fn -D ctype=bfloat16 app.mojo
```
Code:
```mojo
from std.sys import get_defined_dtype
def main() raises:
# ... setup for a typical matmul call
comptime a_type = get_defined_dtype["dtype", DType.bfloat16]()
comptime c_type = get_defined_dtype["ctype", DType.bfloat16]()
matmul[a_type, c_type](a, b, c) # specialized at compile time for this dtype pair
# ... continuing code
```
Unlike C-style `-D` flags, which produce preprocessor strings, Mojo
treats `-D` values as first-class compile-time parameters in the type
system.
This allows the compiler to specialize code such as `matmul[]()` for
every `DType` combination passed on the command line, without runtime
branching.
Values are parsed by the standard library's internal `DType` parser,
which expects canonical names such as `float16`, `bfloat16`, and
`float8_e4m3fn`.
Misspelled or aliased names such as `fp16` and `bf16` don't necessarily
produce compile-time errors at the `get_defined_dtype[]()` call site. They
are parsed as an invalid dtype. An error appears later if that value is used
in a context that rejects it.
If you encounter these errors, check the exact spelling used on the
command line.
## Platform and architecture detection
The `sys.info` module provides compile-time, parameter-evaluable
functions for branching on compilation targets.
### OS detection
Detect the target operating system with:
- `CompilationTarget.is_linux()`
- `CompilationTarget.is_macos()`
Mojo does not currently support Windows targets natively, so there is
no Windows detection API.
### CPU detection
Detect the target CPU architecture or specific Apple Silicon
generation with:
- `CompilationTarget.is_x86()`
- `CompilationTarget.is_arm()`
- `CompilationTarget.is_riscv()`, and `CompilationTarget.is_rv32()` or
`CompilationTarget.is_rv64()` for a specific register width
- `CompilationTarget.is_apple_silicon()`
- `CompilationTarget.is_apple_m1()` through
`CompilationTarget.is_apple_m5()`
### Instruction set detection
Detect target instruction set extensions with APIs such as:
- `CompilationTarget.has_avx512f()`
- `CompilationTarget.has_neon()`
RISC-V has too many extensions for a predicate apiece, so name the extension
instead, using its lowercase LLVM spelling:
- `CompilationTarget.has_riscv_extension["m"]()`
- `CompilationTarget.has_riscv_extension["zba"]()`
### GPU and accelerator detection
Check whether code is compiling *for* a specific accelerator target
with:
- `is_nvidia_gpu()`
- `is_amd_gpu()`
- `is_apple_gpu()`
- `is_gpu()`
Check whether the *host system* has a detected accelerator with:
- `has_accelerator()`
- `has_nvidia_gpu_accelerator()`
- `has_amd_gpu_accelerator()`
- `has_apple_gpu_accelerator()`
The distinction matters:
- `is_nvidia_gpu()` asks: "am I compiling for an NVIDIA GPU?"
- `has_nvidia_gpu_accelerator()` asks whether NVIDIA GPU acceleration
is available. This can also be true when the current compilation
target is NVIDIA GPU.
For example:
```mojo
from std.sys import CompilationTarget
def compute():
comptime if CompilationTarget.has_avx512f():
print("AVX-512 path")
elif CompilationTarget.is_apple_silicon():
print("Apple Silicon path")
else:
print("generic path")
```
These functions report what the compiler is building for. To target a
different platform, set the architecture, CPU, feature set, or accelerator
from the command line. See [Compilation targets](/docs/tools/compilation)
for the available flags and how to query what your toolchain supports.
## Built-in defines
Mojo provides several built-in defines for controlling compilation
and runtime behavior. Other than `ASSERT`, each is populated by the
compiler from a driver flag. Use the driver flag rather than `-D` so
the define matches the compiler's behavior. The flags are shown in
the following table:
| Define | Flag | Type | Values |
|------------------------|---------------------------------|----------|-----------------------------------------------------|
| `__OPTIMIZATION_LEVEL` | `-O` / `--optimization-level` | `Int` | `0`, `1`, `2`, `3` (default `3`) |
| `__DEBUG_LEVEL` | `-g` / `--debug-level` | `String` | `"line-tables"`, `"full"` |
| `__SANITIZE_ADDRESS` | `--sanitize=address` | `Int` | `0` (off, default), `1` (on) |
If `-g` is omitted, `__DEBUG_LEVEL` is not injected; `DebugLevel.level`
returns `"none"` as a library fallback.
`--sanitize` also accepts `thread` (ThreadSanitizer), but only
`--sanitize=address` injects a compile-time define.
Read these values through `sys.compile`, which exposes them as the
compile-time values `OptimizationLevel.level` (an `Int`), `DebugLevel.level`
(a `String`), and `SanitizeAddress` (a `Bool`).
For example:
```mojo
from std.sys.compile import OptimizationLevel
def main():
comptime if OptimizationLevel.level == 0:
print("unoptimized build")
```
Mojo's `ASSERT` flag controls `debug_assert()` behavior:
| Define | Flag | Type | Values |
|----------|---------------------|----------|-----------------------------------------|
| `ASSERT` | `-D ASSERT=` | `String` | `none`, `safe` (default), `all`, `warn` |
`debug_assert()` reads this value directly. Supported assertion levels are:
- `none`: disable all assertions
- `safe` (default in non-debug builds): only run assertions tagged
`assert_mode="safe"`
- `all`: run every `debug_assert()` call
- `warn`: run every assertion, but emit warnings instead of aborting
For example:
```sh
mojo run -D ASSERT=all hello.mojo
```
## Using `debug_assert()` with `-D ASSERT`
`debug_assert()` is a runtime assertion controlled by the `-D ASSERT` flag,
which defaults to `safe`. Other debug-related settings such as `-g` and
`-O` don't affect `debug_assert()` behavior.
The default `safe` mode runs only assertions explicitly tagged as
low-overhead:
```mojo
debug_assert[assert_mode="safe"](
n >= 0,
"nth: n must be non-negative",
)
```
Untagged assertions written as `debug_assert(...)` run under `-D ASSERT=warn`
and `-D ASSERT=all`. Conventionally:
- Tag constant-time checks such as bounds tests and integer comparisons
with `assert_mode="safe"`
- Leave traversals, allocations, and more expensive invariant checks
untagged
The plain `Bool` form always evaluates the condition, even when
assertions are disabled:
```mojo
# Bool form: always evaluates the condition.
debug_assert(len(data) > 0, "data must not be empty")
```
:::caution Apple GPU
`debug_assert()` is silently disabled on Apple GPU targets.
:::
## Debug and optimization
Debug and optimization features are controlled independently; enabling one
does not automatically enable the others. "Debug build" can mean several
different things in Mojo.
**Changes how the compiler emits output**:
| Goal | Flag | Effect |
|-------------------------------------------------|---------------------------------------|-----------------------------------------|
| Emit full debug info (LLDB symbols) | `-g` or `--debug-level=full` | Sets `__DEBUG_LEVEL="full"` |
| Emit line tables only | `-g1` or `--debug-level=line-tables` | Sets `__DEBUG_LEVEL="line-tables"` |
| Disable optimization | `-O0` or `--no-optimization` | Sets `__OPTIMIZATION_LEVEL=0` |
| Enable AddressSanitizer | `--sanitize=address` | Sets `__SANITIZE_ADDRESS=1` |
**Changes the defines seen by Mojo code**:
| Goal | Flag | Effect |
|-------------------------------------------------|---------------------------------------|-----------------------------------------|
| Enable all `debug_assert()` checks | `-D ASSERT=all` | Independent of `-g` and `-O` |
A typical debug configuration combines several of these flags:
```sh
mojo -g -O0 -D ASSERT=all app.mojo
```
The `-g` and `-O` driver flags don't affect `debug_assert()`. If you want
these behaviors together, pass each flag explicitly.
A typical release build requires no special flags. Running:
```sh
mojo app.mojo
```
uses `-O3`, emits no debug info and disables sanitizers.
One exception is `debug_assert()`. Its default mode is `safe`, so
assertions tagged as always-on still execute. Pass `-D ASSERT=none` to
disable them.
## Reading optimization and debug levels
The compiler-defined values `__OPTIMIZATION_LEVEL` and
`__DEBUG_LEVEL` are exposed through `sys.compile` as the compile-time
values `OptimizationLevel` and `DebugLevel`.
Use these when you need fine control:
- `OptimizationLevel.level` stores an integer from `0` to `3`.
- `DebugLevel.level` stores one of the strings `"none"`,
`"line-tables"`, or `"full"`.
```mojo
from std.sys.compile import DebugLevel
def main():
comptime if DebugLevel.level == "full":
print(
"full debug info emitted: enabling source-aware logging"
)
```
:::note
Don't confuse Mojo assert levels with MAX runtime configuration.
The MAX runtime defines its own assertion system through the
`MODULAR_DEBUG=assert-level=...` environment variable, with levels such as
`none`, `warn`, `safe`, and `all`.
These settings control MAX inference runtime assertions. `-D ASSERT`
controls Mojo `debug_assert()` behavior.
The two systems are independent.
:::
---
## Jupyter notebooks
[Jupyter notebooks](https://jupyter.org) provide a web-based environment for
creating and sharing Mojo computational documents. They combine code, results,
and explanation so readers explore what you built, how you built it, and why it
matters.
You can run Mojo language notebooks locally or in GPU-backed Google Colab
environments to accelerate workloads. For teaching, learning, and exploration,
notebooks provide a hands-on, iterative workflow.
This page assumes you'll work with Mojo notebooks in one of two ways:
- **Google Colab**
Fast setup, optional GPU acceleration, ideal for quick experiments and for
learning GPU programming when you don't have a compatible GPU-enabled
computer on-hand.
- **Local JupyterLab**
Private environment with full control of code, data, and dependencies.
Both options use the same notebook model and the same Mojo cell magic.
## Using Mojo on Google Colab
1. Create a Notebook:
Visit [Google Colab](https://colab.google) and create a new notebook.
2. Install Mojo:
For most notebook work, the `mojo` package is all you need. This page
installs `max` instead, because it includes the Mojo compiler—so `%%mojo`
cells behave exactly the same—and it adds the MAX accelerator library that
the [GPU examples](#using-mojo-with-gpu-support) later on this page import.
For the nightly release:
```python
!pip install --pre max --extra-index-url https://whl.modular.com/nightly/simple/
```
For the stable release:
```python
!pip install max
```
Wait for the "Successfully installed" message.
3. Enable Mojo:
In the first cell, run:
```python
import mojo.notebook
```
This adds the `%%mojo` cell magic, so you can compile and
run Mojo code.
Your Colab notebook is now ready to run Mojo programs.
## Using Mojo in Local Jupyter Notebooks
Local notebooks use `pixi` to manage an environment with
Jupyter and Mojo.
1. Create a project:
```shell
pixi init notebooks \
-c https://conda.modular.com/max-nightly/ \
-c conda-forge
cd notebooks
pixi shell
```
This creates a project directory and enters the Pixi shell.
2. Install required tools:
```shell
pixi add max jupyterlab ipykernel
```
This installs:
- Mojo, by way of `max` — see [the note above](#using-mojo-on-google-colab)
on why this page installs `max` rather than `mojo`
- JupyterLab
- The Python kernel required for notebook execution
3. Start JupyterLab:
```shell
jupyter lab
```
JupyterLab opens in your browser.
4. Create a Python-backed notebook:
In your web browser:
- Select _File > New > Notebook_.
- Choose the _Python_ kernel.
5. Enable Mojo support:
In the first cell, run:
```python
import mojo.notebook
```
This registers the `%%mojo` magic command.
Your local environment is now ready for interactive Mojo development.
## Writing and running Mojo code
Mojo code runs inside notebook cells marked with the `%%mojo` directive.
Each Mojo cell must contain a complete program, including a `main()` function.
### Example: Hello Mojo
```mojo
%%mojo
def main():
print("Hello Mojo")
```
Output:
```output
Hello Mojo
```
### Example: Parameterized compilation
```mojo
%%mojo
# Compiler-parameterized function
def repeat[count: Int](msg: String):
comptime for i in range(count):
print(msg)
# Compiler-argumented function
def threehello():
repeat[3]("Hello 🔥!")
# Run
def main():
threehello()
```
Output:
```output
Hello 🔥!
Hello 🔥!
Hello 🔥!
```
## Using Mojo with GPU support
Google Colab offers GPU-backed runtimes so you can run Mojo GPU
examples even without local hardware. The specific accelerator
available depends on your Colab tier; see
[GPU compatibility](/docs/requirements/#gpu-compatibility) for the list
of accelerators supported by Mojo. Before running GPU code, select
_Runtime > Change runtime type > [GPU]_.
### Example: GPU Hello World
```mojo
%%mojo
from max.gpu.host import DeviceContext
def kernel():
print("Hello from the GPU")
def main() raises:
# Launch GPU kernel
with DeviceContext() as ctx:
ctx.enqueue_function[kernel](grid_dim=1, block_dim=1)
ctx.synchronize()
```
Output:
```output
Hello from the GPU
```
### Example: Hello writing
This example writes a value to device memory and reads it back on the host:
```mojo
%%mojo
from std.memory import Pointer
from max.gpu.host import DeviceContext
comptime `✅`: Int32 = 1
comptime `❌`: Int32 = 0
def kernel(value: Pointer[Scalar[.int32], MutAnyOrigin]):
value[unsafe_offset=0] = `✅`
def main() raises:
with DeviceContext() as ctx:
# Build it
var out = ctx.enqueue_create_buffer[.int32](1)
out.enqueue_fill(`❌`)
# Run it
ctx.enqueue_function[kernel](out, grid_dim=1, block_dim=1)
# Report the result
with out.map_to_host() as out_host:
print("GPU responded:", \
"👋, 🔥" if out_host[0] == `✅` else "😢")
```
Output:
```output
GPU responded: 👋, 🔥
```
### Example: GPU vector addition
This example runs elementwise vector addition on the GPU.
Each GPU thread updates one element.
```mojo
%%mojo
from max.gpu import thread_idx
from max.gpu.host import DeviceContext
from layout import TileTensor, row_major
from std.sys import has_accelerator
comptime VECTOR_WIDTH = 10
comptime layout = row_major[VECTOR_WIDTH]()
comptime active_dtype = DType.uint8
# Elementwise vector addition on GPU threads
def vector_addition(
left: TileTensor[active_dtype, type_of(layout), MutAnyOrigin],
right: TileTensor[active_dtype, type_of(layout), MutAnyOrigin],
output: TileTensor[active_dtype, type_of(layout), MutAnyOrigin],
):
var idx = thread_idx.x
output[idx] = left[idx] + right[idx]
def main() raises:
# Ensure a supported GPU (NVIDIA or AMD) is available
comptime assert has_accelerator(), "This example requires a supported GPU"
# Create GPU device context
var ctx = DeviceContext()
# Allocate buffers and tensors for left and right operands, and output
var left_buffer = ctx.enqueue_create_buffer[active_dtype](VECTOR_WIDTH)
var left_tensor = TileTensor(left_buffer, layout)
var right_buffer = ctx.enqueue_create_buffer[active_dtype](VECTOR_WIDTH)
var right_tensor = TileTensor(right_buffer, layout)
var output_buffer = ctx.enqueue_create_buffer[active_dtype](VECTOR_WIDTH)
var output_tensor = TileTensor(output_buffer, layout)
# Initialize input buffers with sample data
var message_bytes: List[UInt8] = [
71, 100, 107, 107, 110, 31, 76, 110, 105, 110
]
with left_buffer.map_to_host() as mapped_buffer:
var mapped_tensor = TileTensor(mapped_buffer, layout)
for idx in range(VECTOR_WIDTH):
mapped_tensor[idx] = message_bytes[idx]
_ = right_buffer.enqueue_fill(1)
# Launch GPU kernel
ctx.enqueue_function[vector_addition](
left_tensor,
right_tensor,
output_tensor,
grid_dim=1,
block_dim=VECTOR_WIDTH,
)
ctx.synchronize()
# Read results back and print as ASCII
with output_buffer.map_to_host() as mapped_buffer:
var mapped_tensor = TileTensor(mapped_buffer, layout)
for idx in range(VECTOR_WIDTH):
print(chr(Int(mapped_tensor[idx])), end="")
print()
```
Output:
```output
Hello Mojo
```
:::tip
Learn Mojo GPU programming through the interactive
[Mojo GPU Puzzles](https://puzzles.modular.com/introduction.html).
:::
---
## Packaging
This page explains how to turn your Mojo project into a distributable conda
package using rattler-build.
You can distribute your conda package on any conda-compatible package index,
such as [prefix.dev](https://prefix.dev), [anaconda.org](https://anaconda.org),
or an S3 bucket. For the most visibility, we recommend sharing your package in
the [modular-community channel](https://prefix.dev/channels/modular-community)
on prefix.dev, as described below.
## How it works
*rattler-build* is a tool that turns your source code into a conda package.
You give it a *recipe*—a YAML file named `recipe.yaml`—and it does the
rest: fetches your source, compiles it in an isolated environment, runs your
tests, and writes out a `.conda` file ready to upload to a package index.
The recipe is a declarative description of your package that specifies:
- The source code location (a git commit or tarball URL)
- The build process (a `mojo precompile` command)
- Package dependencies
- Test commands to verify the build
The complete packaging process is:
1. Create a `recipe.yaml` that specifies your package details.
2. Run `rattler-build` to create a `.conda` package.
3. Share the package in a public package index.
:::note
If you want to distribute your package in the modular-community channel, you
only need to merge your `recipe.yaml` file into the [modular-community
repository](https://github.com/modular/modular-community) (the repo handles
steps 2 and 3).
:::
Then you and other users can install your package with `pixi` or other conda
package managers by adding the appropriate conda channel to your project
manifest file (`pixi.toml`).
## Install rattler-build
We recommend using [Pixi](https://pixi.sh/latest/) to install `rattler-build`:
1. If you don't have it, install `pixi` with this command:
```bash
curl -fsSL https://pixi.sh/install.sh | sh
```
Then restart your terminal for the changes to take effect.
2. Now install `rattler-build` globally:
```bash
pixi global install rattler-build
```
3. Verify the installation:
```bash
rattler-build --version
```
## Write your recipe file
The recipe is the heart of the packaging process and is defined in a YAML file
named `recipe.yaml`.
By convention, store your recipe in your project root at
`conda.recipe/recipe.yaml`. `rattler-build` looks there by default, and it's
the location expected by the GitHub Action
([rattler-build-action](https://github.com/prefix-dev/rattler-build-action)).
For example:
```output
my-mojo-lib/
├── src/
│ └── my_mojo_lib/
│ ├── __init__.mojo
│ └── utils.mojo
├── test.mojo
├── conda.recipe/
│ └── recipe.yaml
├── LICENSE
└── README.md
```
### The minimal recipe file
This section covers the most important recipe fields for Mojo packages.
For details about all available recipe fields, see the [rattler-build recipe
reference](https://rattler-build.prefix.dev/latest/reference/recipe_file/).
You can copy this template to begin building your `recipe.yaml` file:
```yaml title="recipe.yaml"
context:
version: "0.1.0"
package:
name: my-mojo-lib
version: ${{ version }}
source:
- git: https://github.com/yourname/my-mojo-lib.git
rev: a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2
build:
number: 0
script:
- mojo precompile src/my_mojo_lib -o ${{ PREFIX }}/lib/mojo/my_mojo_lib.mojoc
requirements:
build:
- mojo-compiler =25.5.0
host:
- mojo-compiler =25.5.0
run:
- ${{ pin_compatible('mojo-compiler') }}
tests:
- script:
- if: unix
then:
- mojo run test.mojo
files:
recipe:
- test.mojo
about:
homepage: https://github.com/yourname/my-mojo-lib
repository: https://github.com/yourname/my-mojo-lib
license: MIT
license_file: LICENSE
summary: A short one-line description of what your library does.
extra:
maintainers:
- yourname
```
### Recipe tips
Here are a few things that are particularly important for Mojo packages.
#### Use a full commit SHA as the source revision
The `source.rev` field should be a full 40-character git commit SHA rather than
a branch name or tag. This makes the build reproducible—anyone who builds from
the same recipe gets the exact same source code.
#### Set the build number
Start `build.number` at `0`. If you need to rebuild the same version of your
library (such as to pick up a new Mojo compiler release), increment
`build.number` rather than changing the version. Reset it to `0` when you bump
the version.
#### Specify the install location
In the above recipe, look at the `mojo precompile` command in the `build.script`
section. It's important that this command outputs the `.mojoc` file into
`$PREFIX/lib/mojo/`, because this path is what makes the package
auto-discoverable by the Mojo compiler.
When `rattler-build` runs your build script, it sets a `$PREFIX` environment
variable pointing to the root of an isolated installation directory. Any files
your script places under `$PREFIX` become part of the conda package—a file
written to `$PREFIX/lib/mojo/foo.mojoc` during the build is extracted into your
actual environment when you run `pixi add foo`. Think of `$PREFIX` as a
stand-in for wherever your environment lives on your machine.
#### Pin the Mojo compiler version
Precompiled Mojo files compile against a specific compiler version and might not
be compatible with other versions. The required `mojo-compiler` version must be
specified in the `requirements.build` section of your recipe, which must conform
to the
[conda package match syntax](https://docs.conda.io/projects/conda/en/latest/user-guide/concepts/pkg-specs.html#package-match-specifications).
The `pin_compatible('mojo-compiler')` function
in `requirements.run` generates a version constraint based on whichever version
is resolved at build time, preventing your package from silently running
against an incompatible runtime.
## Build the package
With your recipe file in hand, you can build the package using `rattler-build`
from your project root:
```bash
rattler-build build \
--recipe conda.recipe/recipe.yaml \
-c conda-forge \
-c https://conda.modular.com/max \
-c https://repo.prefix.dev/modular-community
```
The `-c` flags specify which conda channels to search for dependencies (in
priority order). You need:
- `conda-forge` for general tooling
- `https://conda.modular.com/max` for `mojo-compiler` and `max`
- `https://repo.prefix.dev/modular-community` if you depend on other
community Mojo packages
When you run `rattler-build build`, it:
1. Creates an isolated build environment
2. Fetches your source code
3. Runs your build script to compile the `.mojoc` file
4. Bundles the result into a `.conda` archive
5. Runs your test commands to verify the package works
The output file appears in an `output/` directory, for example:
```text
output/
└── linux-64/
└── my-mojo-lib-0.3.0-h1a2b3c4_0.conda
```
The hash in the filename (`h1a2b3c4`) is derived from the build configuration
and is managed automatically by rattler-build.
## Debug a failed build
If the build fails, open a debug shell to investigate interactively:
```bash
rattler-build debug shell
```
This gives you a shell with all environment variables set (`$PREFIX`,
`$SRC_DIR`, etc.) and the build environment activated, so you can run your
build commands to find the problem.
For more details, see the [rattler-build debugging
guide](https://rattler-build.prefix.dev/latest/debugging_builds/).
## Publish to a package index
Once you have a built `.conda` file, you can upload it to any compatible host,
such as [prefix.dev](https://prefix.dev), [anaconda.org](https://anaconda.org),
or an AWS S3 bucket. For the most visibility, add your package to the
[modular-community channel](https://prefix.dev/channels/modular-community)
(hosted on prefix.dev), as described below.
### Publish to the modular-community channel
To publish your package on the [modular-community
channel](https://prefix.dev/channels/modular-community), open a pull
request to the [modular-community GitHub
repo](https://github.com/modular/modular-community) to add your package's
`recipe.yaml` file. The repo automatically builds and hosts all the packages
based on the recipes in the repo.
Your `recipe.yaml` is the same file described above. Just add it to a new
directory that matches your package name:
```text
modular-community/
└── recipes/
└── my-mojo-lib/
└── recipe.yaml
```
Once published to the channel, you can install your package with `pixi` by
adding the `https://repo.prefix.dev/modular-community` channel to your
project manifest:
```toml title="pixi.toml"
[workspace]
channels = [
"https://conda.modular.com/max-nightly",
"https://repo.prefix.dev/modular-community",
"conda-forge",
]
```
:::note
If your package includes Python or any language other than Mojo, you must enable
[CodeQL scanning](https://docs.github.com/en/code-security/code-scanning/enabling-code-scanning/configuring-default-setup-for-code-scanning)
on your source repository and add the badge to your README.
:::
For more details, see the
[modular-community README](https://github.com/modular/modular-community?tab=readme-ov-file#modular-community-channel).
### Update your package
When you release a new version of your library:
1. Update `context.version` in your recipe.
2. Update `source.rev` to the new commit SHA (or update the tarball URL and
SHA256).
3. Reset `build.number` to `0`.
4. Open a new PR to modular-community (if you've already published it there).
If you're republishing the same version (for example, to support a new Mojo
compiler release), increment `build.number` instead of changing the version.
## Useful links
- [modular-community repository](https://github.com/modular/modular-community)
- [rattler-build recipe reference](https://rattler-build.prefix.dev/latest/reference/recipe_file/)
---
## Mojo AI skills
Mojo is ideal for agentic programming because it has a concise language
syntax and your agent will catch most of the coding errors at compile time.
To make your token usage even more efficient, our Mojo skills ensure
that you generate up-to-date and idiomatic Mojo code from the start.
Many AI models are trained on older versions of Mojo and MAX.
They aren't updated as quickly as the language evolves, so they
often generate code that doesn't compile or reflects outdated
usage.
For best results, agents need accurate, up-to-date context.
Mojo agent skills are designed to be compact and focused,
providing only the most important guidance needed to avoid
common code generation issues. This keeps token usage low and
leaves more room for relevant context.
[Modular skills](https://github.com/modular/skills/tree/main)
provide current guidance on Mojo syntax, development patterns,
and workflows so AI coding agents generate modern, working
code that aligns with the language today.
## What you can do with this
- Start new Mojo or MAX projects without manual setup
("I want to start a new Mojo project for image enhancement")
- Generate modern Mojo syntax
("Write a function that applies a transformation around the center")
- Write GPU code using valid patterns
("Convert this CPU function to run on GPU")
- Use Python interoperability correctly
("Update this Mojo code to use NumPy")
- Port code from CUDA, Python, or C++
("Convert this CUDA function to Mojo")
You describe the goal. Your system handles the language.
## Installation
**Install all skills**:
```text
npx skills add modular/skills
```
**Install a specific skill**:
```text
npx skills add modular/skills --skill mojo-syntax
```
**Update skills**:
```text
npx skills update
```
### Manual installation
**HTTPS:**
```text
git clone https://github.com/modular/skills.git
```
**SSH:**
```text
git clone git@github.com:modular/skills.git
```
**CLI:**
```text
gh repo clone modular/skills
```
### Configuration
Copy or symlink individual skill files into your agent's configuration
directory.
## How it works
Skills follow the
[Agent Skills Standard](https://agentskills.io/specification).
Each skill is self-contained, triggered by intent, and
structured for reliable use by AI agents.
At runtime:
1. The agent interprets your request.
2. It selects the right skill (for example, `mojo-syntax`
or `mojo-gpu-fundamentals`).
3. The skill guides generation toward current Mojo and
MAX patterns.
This isn't prompting. It's controlled code generation.
## Connect to the docs MCP server
Connect the docs
[Model Context Protocol](https://modelcontextprotocol.io) (MCP) server to give
your AI assistant live access to Mojo's documentation. Your assistant can
search Mojo's manual, API references, and code examples while it plans, writes,
and debugs your code, so its answers stay grounded in the current
documentation instead of its training data.
Your assistant can usually set up the MCP server itself with a prompt like
this:
```text
Add the Mojo docs MCP server at https://mojo-mcp.modular.com/mcp/ and verify it
by searching the docs.
```
Or configure it manually in your tool's settings.
With Claude Code, add the server from the command line:
```sh
claude mcp add --transport http mojo-docs https://mojo-mcp.modular.com/mcp/
```
With Cursor, add the server to `~/.cursor/mcp.json`:
```json
{
"mcpServers": {
"mojo-docs": {
"url": "https://mojo-mcp.modular.com/mcp/"
}
}
}
```
Any other client that supports MCP's streamable HTTP transport connects with
the same URL. The server indexes both the stable and nightly documentation.
## FAQ
**Are the skills always up to date with the latest Mojo?**
Skills are updated regularly to track changes in Mojo and MAX, but
there will be lag between language changes and skill updates.
**Can I select a skill version that matches my installed Mojo version?**
Not currently. Skills aren't versioned by Mojo release, so there may be
mismatches between the skill's guidance and your installed version.
We recommend installing the latest version of Mojo to minimize this risk.
**Do I have to install all the skills?**
No. Install only what you need.
**Are the skills licensed?**
Yes. They're available under the Apache 2.0 license.
---
## Testing
Mojo includes a framework for developing and executing unit tests. The Mojo
testing framework consists of a set of assertions defined as part of the [Mojo
standard library](/docs/std) and the
[`TestSuite`](/docs/std/testing/suite/TestSuite/) struct for automatic test
discovery and execution.
## Get started
Let's start with a simple example of writing and running Mojo tests.
### 1. Write tests
For your first example of using the Mojo testing framework, create a file named
`test_quickstart.mojo` containing the following code:
```mojo
# Content of test_quickstart.mojo
from std.testing import assert_equal, TestSuite
def inc(n: Int) -> Int:
return n + 1
def test_inc_zero() raises:
# This test contains an intentional logical error to show an example of
# what a test failure looks like at runtime.
assert_equal(inc(0), 0)
def test_inc_one() raises:
assert_equal(inc(1), 2)
def main() raises:
TestSuite.discover_tests[__functions_in_module()]().run()
```
In this file, the `inc()` function is the test *target*. The functions whose
names begin with `test_` are the tests. Usually you should define the target in
a separate source file from its tests, but you can define them in the same file
for this simple example.
A test function *fails* if it raises an error when executed, otherwise it
*passes*. The two tests in this example use the `assert_equal()` function,
which raises an error if the two values provided are not equal.
:::note
The implementation of `test_inc_zero()` contains an intentional logical error
so that you can see an example of a failed test when you execute it in the
next step of this tutorial.
:::
### 2. Execute tests
Then in the directory containing the file, execute the following command in your
shell:
```bash
mojo run test_quickstart.mojo
```
You should see output similar to this (note that this example elides the full
filesystem paths from the output shown):
```output
Unhandled exception caught during execution:
Running 2 tests for ROOT_DIR/test_quickstart.mojo
FAIL [ 0.009 ] test_inc_zero
At ROOT_DIR/test_quickstart.mojo:40:5: AssertionError: `left == right` comparison failed:
left: 1
right: 0
PASS [ 0.001 ] test_inc_one
--------
Summary [ 0.009 ] 2 tests run: 1 passed , 1 failed , 0 skipped
Test suite 'ROOT_DIR/test_quickstart.mojo' failed!
mojo: error: execution exited with a non-zero result: 1
```
The output shows each test as it runs with PASS or FAIL status and execution
time, followed by a summary of tests run, passed, failed, and skipped. Failed
tests display their error messages inline.
### Next steps
- [Using Mojo assertion functions](#using-mojo-assertion-functions) describes
the assertion functions available to help implement tests.
- [Writing unit tests](#writing-unit-tests) shows how to write unit tests and
organize them into test files.
- Our GitHub repo contains an [example
project](https://github.com/modular/modular/tree/mojo/v1.1.0/Mojo/examples/testing)
to demonstrate unit testing. Several of the examples shown later are based on
this project.
## Using Mojo assertion functions
The Mojo standard library includes a [`testing`](/docs/std/testing/testing/)
module that defines several assertion functions for implementing tests. Each
assertion returns `None` if its condition is met or raises an error if it isn't.
- [`assert_true()`](/docs/std/testing/testing/assert_true/):
Asserts that the input value is `True`.
- [`assert_false()`](/docs/std/testing/testing/assert_false/):
Asserts that the input value is `False`.
- [`assert_equal()`](/docs/std/testing/testing/assert_equal/):
Asserts that the input values are equal.
- [`assert_not_equal()`](/docs/std/testing/testing/assert_not_equal/):
Asserts that the input values are not equal.
- [`assert_almost_equal()`](/docs/std/testing/testing/assert_almost_equal/):
Asserts that the input values are equal up to a tolerance.
The boolean assertions report a basic error message when they fail.
```mojo
from std.testing import *
assert_true(False)
```
```output
Unhandled exception caught during execution
Error: At Expression [1] wrapper:14:16: AssertionError: condition was unexpectedly False
```
Each function also accepts an optional `msg` keyword argument for providing a
custom message to include if the assertion fails.
```mojo
assert_true(False, msg="paradoxes are not allowed")
```
```output
Unhandled exception caught during execution
Error: At Expression [2] wrapper:14:16: AssertionError: paradoxes are not allowed
```
For comparing floating-point values, you should use `assert_almost_equal()`,
which allows you to specify either an absolute or relative tolerance.
```mojo
var result = 10 / 3
assert_almost_equal(result, 3.33, atol=0.001, msg="close but no cigar")
```
```output
Unhandled exception caught during execution
Error: At Expression [3] wrapper:15:24: AssertionError: 3.3333333333333335 is not close to 3.3300000000000001 with a diff of 0.0033333333333334103 (close but no cigar)
```
The testing module also defines a [context
manager](/docs/manual/errors#use-a-context-manager),
[`assert_raises()`](/docs/std/testing/testing/assert_raises/), to assert that
a given code block correctly raises an expected error.
```mojo
def inc(n: Int) raises -> Int:
if n == Int.MAX:
raise Error("inc overflow")
return n + 1
print("Test passes because the error is raised")
with assert_raises():
_ = inc(Int.MAX)
print("Test fails because the error isn't raised")
with assert_raises():
_ = inc(Int.MIN)
```
```output
Unhandled exception caught during execution
Test passes because the error is raised
Test fails because the error isn't raised
Error: AssertionError: Didn't raise at Expression [4] wrapper:18:23
```
:::note
The example above assigns the return value from `inc()` to a
[*discard pattern*](/docs/manual/lifecycle/death/#explicit-lifetime-extension).
Without it, the Mojo compiler reports a warning that the return value is unused.
:::
You can also provide an optional `contains` argument to `assert_raises()` to
indicate that the test passes only if the error message contains the substring
specified. Other errors are propagated, failing the test.
```mojo
print("Test passes because the error contains the substring")
with assert_raises(contains="required"):
raise Error("missing required argument")
print("Test fails because the error doesn't contain the substring")
with assert_raises(contains="required"):
raise Error("invalid value")
```
```output
Unhandled exception caught during execution
Test passes because the error contains the substring
Test fails because the error doesn't contain the substring
Error: invalid value
```
## Writing unit tests
A Mojo unit test is simply a function that fulfills all of these requirements:
- Has a name that starts with `test_` for automatic discovery.
- Accepts no arguments.
- Returns `None`.
- Raises an error to indicate test failure.
- Is defined at the module scope, not as a Mojo struct method.
Generally, you should use the assertion utilities from the Mojo standard library
[`testing`](/docs/std/testing/testing/) module to implement your tests.
You can include multiple related assertions in the same test function. However,
if an assertion raises an error during execution, then the test function returns
immediately, skipping any subsequent assertions.
## Running tests with TestSuite
To run your tests, each test file must include a `main()` function that uses
[`TestSuite.discover_tests()`](/docs/std/testing/suite/TestSuite/#discover_tests)
to automatically discover and execute all test functions in the module. The
`__functions_in_module()` compiler intrinsic provides a list of all functions
defined in the current module, which `discover_tests()` filters to find those
with the `test_` prefix.
Here is an example of a test file containing three tests for functions defined
in a source module named `my_target_module` (which is not shown here).
```mojo
# File: test_my_target_module.mojo
from my_target_module import convert_input, validate_input
from std.testing import assert_equal, assert_false, assert_raises, assert_true, TestSuite
def test_validate_input() raises:
assert_true(validate_input("good"), msg="'good' should be valid input")
assert_false(validate_input("bad"), msg="'bad' should be invalid input")
def test_convert_input() raises:
assert_equal(convert_input("input1"), "output1")
assert_equal(convert_input("input2"), "output2")
def test_convert_input_error() raises:
with assert_raises():
_ = convert_input("garbage")
def main() raises:
TestSuite.discover_tests[__functions_in_module()]().run()
```
You can then use `mojo run test_my_target_module.mojo` to run the tests and
report the results.
## Filtering tests
By default, a `TestSuite` runs every test discovered in a test file. You can
filter which tests run, either from the command line or programmatically. This
is useful when you want to focus on a single failing test, exclude a
known-broken or flaky test, or list the tests in a file without running them.
The examples in this section use the test files from the [example
project](https://github.com/modular/modular/tree/mojo/v1.1.0/Mojo/examples/testing),
which defines the tests `test_inc_valid()` and `test_inc_max()` in
`test/my_math/test_inc.mojo`.
### Filter tests from the command line
A test file that uses `TestSuite` accepts the following command line flags:
- `--skip `: Run all tests *except* the named ones (a skip-list).
- `--only `: Run *only* the named tests (an allow-list).
- `--skip-all`: Skip every test, collecting and listing the tests without
running any of them.
For example, to run every test in `test_inc.mojo` except `test_inc_max()`:
```bash
mojo run -I src test/my_math/test_inc.mojo --skip test_inc_max
```
Skipped tests appear in the output with `SKIP` status:
```output
Running 2 tests for test/my_math/test_inc.mojo
PASS [ 0.001 ] test_inc_valid
SKIP [ 0.001 ] test_inc_max
--------
Summary [ 0.001 ] 2 tests run: 1 passed , 0 failed , 1 skipped
```
To run only `test_inc_valid()`, use the `--only` flag:
```bash
mojo run -I src test/my_math/test_inc.mojo --only test_inc_valid
```
To collect and list the tests without running any of them, use `--skip-all`.
This is handy when you want to see which tests a file contains:
```bash
mojo run -I src test/my_math/test_inc.mojo --skip-all
```
A few things to note about these flags:
- The `--skip` and `--only` flags each accept multiple test names as separate,
space-separated arguments. For example, `--skip test_inc_valid test_inc_max`
skips both tests. Don't combine the names into a single quoted argument.
- Test names must match exactly. If you pass a name that doesn't correspond to a
discovered test, the suite raises an error and exits with a non-zero status.
- You can use only one of these flags per command. The `--skip-all` flag takes
no test names.
### Skip tests programmatically
Instead of, or in addition to, filtering from the command line, you can skip
specific tests in the test file itself. Capture the suite returned by
`discover_tests()` in a variable and call
[`skip()`](/docs/std/testing/suite/TestSuite/#skip) before running the suite:
```mojo
def main() raises:
var suite = TestSuite.discover_tests[__functions_in_module()]()
suite.skip[test_inc_max]()
suite^.run()
```
Note the `^` transfer sigil in the call to
[`run()`](/docs/std/testing/suite/TestSuite/#run): the method consumes the
suite, so you must transfer ownership of the suite to it.
To skip more than one test, call `skip()` once for each test you want to skip:
```mojo
def main() raises:
var suite = TestSuite.discover_tests[__functions_in_module()]()
suite.skip[test_broken]()
suite.skip[test_flaky]()
suite^.run()
```
A programmatic skip always takes effect, even when you use `--only` to allow a
test that's also skipped in the code. This makes `skip()` a good fit for tests
that you want to keep disabled regardless of the command line filters, such as a
test that's broken, flaky, or that depends on an unavailable environment.
For more information, see the
[`TestSuite`](/docs/std/testing/suite/TestSuite/) API reference.