# Mojo language reference > Mojo language reference covering keywords, literals, operators, expressions, statements, declarations, and built-in decorators. Version: 1.1.0 This file contains all documentation content in a single document following the llmstxt.org standard. ## Mojo basics cheat sheet Sheet, Panel, } from '@site/src/components/CheatSheets/sheet'; {/* markdownlint-disable MD033 MD034 */} ```mojo def main(): print("Hello, Mojo!") ``` Run it: **mojo hello.mojo**. Every program starts at **main()**. ```mojo # Line comment def greet(): """Docstring: what greet does.""" print("hi") ``` ```mojo var count = 0 # owned, inferred Int var name: String = "Mojo" count = count + 1 # var is mutable var data: List[Int] = [1, 2, 3] ref view = data[0] # ref to an element, no copy view = 99 # writes through to data comptime PI = 3.14159 # compile-time const ``` **var** declares an owned value, mutable by default. **ref** is a reference to a value it doesn't own. Mutable changes update the original value. ```mojo Float32 == Scalar[DType.float32] == SIMD[DType.float32, 1] ``` | Type | Meaning | |--------------|----------------------------------------------------------------------------------------------------| | Int | machine-word integer; default index type | | UInt | machine-word | | Int8 … Int64 | sized integers | | Float64 | default floating point (also Float32, Float16, and special-purpose 8-bit FP8 and 4-bit FP4 floats) | | Bool | True / False | | String | UTF-8, supports Unicode graphemes | | List[T] | growable, homogeneous sequence | | SIMD[dt, n] | n-wide numeric vector | Numeric types are **SIMD** vectors under the hood: scaling to vector math is built in. No implicit numeric conversion: cast explicitly with **Float64(n)**, **Int(x)**, **String(v)**. Use **.cast** for SIMD vectors. Types are **PascalCase** (**Int**); names are **lower_snake_case**. | Op | Meaning | |-----------------------|----------------------------------| | + - * / | add, subtract, multiply, divide | | // % | floor divide, modulo | | ** | power (2 ** 10), also pow(2, 10) | | == != | equal, not equal | | < <= > >= | comparisons (chainable) | | and or not | logical, short-circuit | | += -= | compound assign (\*=, /=, …) | ```mojo a < b < c # chains to (a < b) and (b < c) ``` ```mojo var who = "Mojo" print("Hi, " + who) # concatenation print(t"Hi, {who}!") # interpolation print(1, 2, 3, end=": ") # keyword args var s = String(t"x = {1 + 1}") # to String var raw = r"C:\path" # raw string ``` **print** takes a t-string directly; cast with **String(...)** to use one elsewhere. Triple quotes make multi-line strings. ```mojo if x > 0: print("positive") elif x == 0: print("zero") else: print("negative") # ternary var kind = "even" if x % 2 == 0 else "odd" ``` ```mojo for i in range(5): # 0 1 2 3 4 print(i) for item in [10, 20, 30]: # iterate an array if item == 20: continue # skip to next if item == 30: break # stop the loop print(item) while n > 0: # loop while true print(n) n -= 1 ``` Repeat n times with **for \_ in range(n)** (**\_** discards the value). ```mojo def add(a: Int, b: Int) -> Int: return a + b def greet(name: String = "world"): # default print(t"Hi, {name}") def risky() raises: # may raise raise Error("boom") def nothing(): pass # do-nothing body ``` No `->` means the function returns **None**. `def` can raise only if marked **raises**, so callers can see it coming. ```mojo from std.math import sqrt # one name from std.math import sqrt as root # aliased ``` Built-ins like **Int**, **String**, **List**, and **print** are in the Mojo prelude, no import needed. ```mojo var xs: List[Int] = [1, 2, 3] xs.append(4) print(xs[0]) # 1 print(len(xs)) # 4 ``` Always annotate **List[T]**: a bare bracket literal infers a fixed-size **Array**, which has no **append**. ```mojo @fieldwise_init # synthesizes __init__ struct Point: var x: Int var y: Int struct Counter: var n: Int def __init__(out self): # builds self self.n = 0 def bump(mut self): # modifies self self.n += 1 def main(): var p = Point(3, 4) print(p.x, p.y) # 3 4 ``` Every instance method takes **self** as its first argument. The **out** convention returns the initialized **self** without a return arrow. Structs also support **comptime** constants and static methods (**@staticmethod**). ```mojo def risky() raises: raise Error("boom") def main(): try: risky() except e: print("caught:", e) ``` Mark a raising function **raises**; **raise** signals, **try**/**except** catches. **except e** binds the error. - Every value has a fixed type. No implicit numeric conversion: write `Float64(n)`, `Int(x)`. - Assignment copies the value; it is not a shared reference. - String interpolation is `t"…"`, not `f"…"`. - Declare with **var** (and **ref**) rather than a bare `x = 5`. - No **match** yet: use `if/elif/else`. - A `def` with no `->` returns **None**, same as Python. - Python-style syntax: indentation and `def`, no braces, semicolons, or headers. - Value semantics with moves: `^` transfers ownership (like `std::move` or a Rust move); `__deinit__` gives RAII cleanup. - Behavior comes from **traits**, not class inheritance or templates (like Rust traits or C++20 concepts). - No `?:` ternary or Elvis operator: write `a if cond else b`. - No **switch** yet: use `if/elif/else`. --- ## Mojo compile-time cheat sheet Sheet, Panel, } from '@site/src/components/CheatSheets/sheet'; {/* markdownlint-disable MD033 MD034 */} - One language. Compile time and runtime use the same Mojo. - The compiler acts on what it can prove, using constraints and conformance rules in code. - Proven facts enable or disable methods, constructors, and conformances. - Compile-time computation shifts expensive work out of runtime. ```mojo def repeat[T: Copyable, n: Int](x: T) -> Array[T, n]: ... # T is a type, n is a value struct Matrix[dtype: DType, rows: Int, cols: Int]: ... ``` Use parameterized declarations for functions and structs. The compiler creates a concrete implementation for each unique set of parameter values. ```mojo def sort(mut self) where conforms_to(Self.T, Comparable): ... struct Box[T](Writable where conforms_to(T, Writable)): ... def chunk[w: Int]() where w.is_power_of_two(): ... ``` A precondition the compiler must prove, such as trait facts, numeric truths, or a DType's kind, before the call compiles. ```mojo def largest[T: Comparable & Copyable](xs: List[T]) -> T: ... # only operations T guarantees will compile ``` A trait conformance guarantees what capabilities the parameters support, so only valid code compiles. ```mojo struct Buffer[T: Copyable, n: Int]( Writable where conforms_to(T, Writable), # conforms only if T does ): def first(self) -> Self.T where Self.n > 0: ... # method only if n > 0 ``` A type, method, conformance, or comptime declaration is available only when the compiler can prove its condition. The API is correct by construction: a missing capability or unmet constraint means calls with invalid parameters won't compile. ```mojo comptime name = reflect[T].name() # also .field_count(), .field_names(), ... comptime t = type_of(x) # the type of an expression ``` `reflect[T]` reads a type's structure and `type_of` an expression's type, so parameterized code adapts to any shape. ```mojo def meters(ft: Float64) -> Float64: return ft * 0.3048 comptime track = meters(100.0) # runs while compiling, baked in ``` Every fact must be established at compile time. ```mojo comptime MAX = 2 ** 200 # arbitrary-precision integer comptime c = 0.1 + 0.2 # 0.3 exactly: a literal, kept exact var r = 0.1 + 0.2 # a Float64, subject to rounding ``` Literals stay exact. `Float64` rounds values like `0.1`, and repeated computations accumulate rounding error. Compute accuracy-sensitive constants at compile time. ```mojo def slow_calc() -> Float64: ... # an expensive calculation comptime FACTOR = slow_calc() # computed while compiling, baked in ``` Run expensive computation once while compiling; the result is baked in, free at runtime. For tables and other compile-time data, `global_constant` gives O(1) access without materializing them each time. ```mojo comptime if is_nvidia_gpu(): # only the live branch compiles use_nvidia() else: use_fallback() comptime for i in range(4): # fully unrolled process[i]() ``` `comptime if` compiles the live branch only; `comptime for` unrolls, removing loop overhead. ```mojo comptime w = simd_width_of[DType.float32]() # lanes that fit a register size_of[T]() align_of[T]() # layout, at compile time ``` `sys.info` answers machine questions at compile time, so one source adapts to every target. ```mojo comptime table: List[Int] = [3, 5, 7, 11, 13] # a comptime List (heap-backed) var t = materialize[table]() # -> a runtime List; you choose when it allocates ref g = global_constant[POWERS]() # POWERS: a fixed scalar table, read g[i], no copy ``` Scalars materialize automatically; heap-backed values (`List`, `Dict`) need `materialize`. `global_constant` keeps one static copy to index. ```mojo struct Stack[T: Copyable]: comptime Element = Self.T # associated type comptime capacity = 1024 # comptime value member comptime Scalar[dt: DType] = SIMD[dt, 1] # parametric alias ``` A type carries its own compile-time members (values, associated types, and parametric aliases), reached through `Self`. ```mojo @always_inline def lerp(a: Float64, b: Float64, t: Float64) -> Float64: return a + (b - a) * t # expanded at every call site @no_inline def cold_path(): ... # kept as a real call ``` Inlining replaces a function call with the function body, reducing call overhead for small, frequently called functions. Add `@always_inline` to request inlining, `@no_inline` to exclude the option, or let the compiler decide. - Everything used in compile-time code must be known at compile time. A compile-time value, parameter, `if`, or `for` can't depend on runtime input. - At compile time, you can't perform file I/O, make foreign calls, or call functions that can raise. - Compile-time code runs on the CPU, like all compilation. --- ## Mojo conversions cheat sheet Sheet, Panel, Intro, } from '@site/src/components/CheatSheets/sheet'; {/* markdownlint-disable MD033 MD034 */} ## Numbers make ```mojo 42 # IntLiteral Int(x) # any Intable: Bool, Int8 … Int(s) # any Intable that's raising: String Int(Int32(5)) # any numeric scalar, cross-dtype ``` convert ```mojo Float64(i), UInt(i) # to float, to unsigned (same bits) String(i), Bool(i) # to text, True if non-zero i.cast[DType.int8]() # to another dtype ``` Float to Int truncates toward zero; cross-dtype casts wrap (two's complement). **Int** is **Scalar[DType.int]**, so **.cast** works like any SIMD scalar. **Int** and **UInt** share bits: Int(-1) is UInt 2^64-1. make ```mojo 3.14 # FloatLiteral Float64(x) # any Floatable: Int, Bool Float64(s) # any Floatable that's raising: String Float32(x).cast[DType.float64]() # widen a scalar ``` convert ```mojo Int(f) # truncates toward zero f.cast[DType.float32]() # narrow (precision loss) Bool(f), String(f) # True if non-zero, to text ``` No bare **Float**, and no Float128/256. Convert all other floats (including special purpose) with **.cast()**. make ```mojo Bool(x) # any Boolable ``` convert ```mojo Int(b), Float64(b), String(b) # 0 or 1, 0.0 or 1.0, "True" or "False" ``` truthy — for if / while / and / or / Bool() | Kind | Truthy when | |---------------------------|-----------------------| | **Numbers** | non-zero | | **Strings & collections** | non-empty | | **Optional** | None False, else True | | **PythonObject** | Python's own rules | Any type with a **\_\_bool\_\_** is truthy. Converting a **Bool** to a number is explicit: **Int(b)**, never implicit. make ```mojo SIMD[T, N](x) # splat one value to all lanes SIMD[T, 4](a, b, c, d) # per-lane ``` convert ```mojo v.cast[DType.x]() # new dtype, same lane count SIMD[T, N](scalar) # splat a Scalar up to N lanes ``` **N** is the lane count, not bit width; a lane's bit width is its **DType**. Int, Float64, Int8 … are all SIMD scalars. ## Text make ```mojo String(x) # any StringSpan, StringLiteral, Writable (Int, Float64, Bool …) String(t"{x}") # Not needed for print() String(from_utf8=bytes) # raises on bad UTF-8 String(from_utf8_lossy=bytes) # replaces bad bytes ``` convert ```mojo Int(s) # base-10 parse; raises on "3.5", "0xff", "" Float64(s) # parse (1e3, inf ok); raises "", garbage Bool(s) # True if non-empty ``` access (by byte / codepoint / grapheme) ```mojo s[byte=i], s[byte=i:j] # also for codepoint and single index grapheme s.as_bytes(), s.codepoints(), s.graphemes() # iterators ``` ## Pointers Use **unsafe_ptr()** to access: **List**, **String**, **StringSpan**, **Array**, and **Span**. access through a pointer ```mojo buf.unsafe_ptr() # -> Pointer[T] p[unsafe_offset=i] # deref one element p.unsafe_offset(i)[] # pointer arithmetic, then deref p.unsafe_load[width=N]() # read N lanes -> SIMD[T, N] ``` vectorize a buffer (the escape hatch) ```mojo var v = data.unsafe_ptr().unsafe_load[width=8]() # 8 elements -> one SIMD var total = v.reduce_add() # SIMD-wide reduce ``` **Pointer** is non-null by design. Use **OptionalPointer** for a nullable pointer. Unsafe operations carry the **unsafe\_** prefix or an **unsafe\_** keyword. ## Collections make ```mojo var x: List[T] = [a, b, c] # annotate: unannotated defaults to Array List[T](capacity=n) # empty; initial room for n List[T](length=n, fill=x) # n copies of x (T: Copyable) List(range(n)) # materialize a range List(iterable) # from any iterator / iterable ``` access ```mojo list[i], list[i:j] # element by ref, Span view (no copy) list.unsafe_take_allocation() # hand off the buffer as an Allocation[T] ``` make ```mojo Dict[K, V]() # empty; fill with d[k] = v Dict[K, V](capacity=n) # empty; initial room for n Dict.fromkeys(keys, v) # every key maps to v ``` access ```mojo d.setdefault(key, default) # ref; inserts default if absent d.get(key) d.find(key) # Optional[V] d.keys() d.values() # lazy iterators d.items() # iterator of DictEntry (.key / .value) d.pop(key) # value, removes it ``` make ```mojo Optional(x) # from a value (T inferred from x) Optional[T](), Optional[T](None) # empty ``` access ```mojo o.value(), o.take(), o[] # ref, move out, ref (abort, abort, raise) o.or_else(default) # value, or default ``` --- ## Mojo cheat sheets Quick-reference cards for Mojo syntax and types. --- ## Mojo ownership cheat sheet Sheet, Panel, Yes, No, Partial, } from '@site/src/components/CheatSheets/sheet'; {/* markdownlint-disable MD033 MD034 */} Value ownership is fundamental to Mojo. Every value has exactly one owner, and how values move between owners runs through the whole language. You encounter ownership in two situations: **variables** and **function calls**. Variables can own or reference a value. **Argument conventions** describe how a function uses a value: read-only, reference, mutable, owned, produced, or consumed. ```mojo var data: List[Int] = [1, 2, 3] # owns the list ref view = data[0] # a 2nd name, no copy view = 9 # writes through to data print(data) # [9, 2, 3] ``` **var** always means "I own this." **ref** means "this is a view into someone else's value." A struct's **var** field owns its value and a struct type owns its fields. A **var** assignment uses the right-hand side's policy: it determines whether the value is materialized, constructed, copied, or transferred. A call or expression returning a value constructs; one returning a reference copies out the referenced value. | You write | The var takes ownership of | |---------------------------------------|---------------------------------| | 5.0 / "Hello" / [1, 2, 3] (a literal) | a materialized literal | | SomeType() | a freshly constructed value | | some_value (ImplicitlyCopyable) | an implicit copy | | some_value.copy() (Copyable) | an explicit copy | | some_value^ (Movable) | the source's value, transferred | | some_ref | a copy of the referenced value | A copy doesn't change a value's ownership. Only **^** moves the value to a new owner. | Convention | Meaning | |-------------|------------------------------| | self | imm (immutable) | | mut self | modify the instance | | out self | build it (in \_\_init\_\_()) | | deinit self | destroy the instance | | ref self | parametric mutability | ```mojo def exclaim(var s: String): s += "!" print(s) var g = "Hello" exclaim(g) # copy: g still usable exclaim(g^) # transfer: g uninitialized # print(g) # error: used after transfer ``` The **var** argument takes ownership of the original only with **^**; a plain call implicitly copies (**String** is **ImplicitlyCopyable**), so **g** stays usable. Either value, the copy or the transferred original, ends its lifetime after the **print** (its last use). The same **^** drains a collection in a loop: **for var x in items^** moves each element out. | You write | Into a var arg | |-------------|---------------------------------------------| | f(x) | implicit copy (**ImplicitlyCopyable** only) | | f(x.copy()) | explicit copy | | f(x^) | transfer; x uninitialized after | A borrowing argument (**imm**, **mut**, **ref**) has no **^** lever: you write **f(x)**, and it views the value in place. ```mojo def first[T: Movable](ref xs: List[T]) -> ref[xs[0]] T: return xs[0] ref x = first(xs) # len(xs) known to be > 0 ``` A **ref** return carries an **origin** so the compiler tracks where it points, whether it stays valid, and whether access is mutable. Values are destroyed at last use; a live **ref** keeps the value it refers to alive. | Convention | Owns it? | Mutable? | Caller keeps it? | Reach for it when | |------------|------------------------------------|-------------------------------|-------------------------------------|---------------------------------------------------| | (imm) | | | | reading a value without changing it (the default) | | mut | | | | changing the caller's value in place | | var | (own copy) | | yes, unless ^ | you need a local, mutable copy | | out | (becomes the value) | | it is the result | returning by name instead of -> | | deinit | (consumes) | | | destructors and the source of a move | | ref | (refers) | parametric | | returning or holding a reference with an origin | A convention sits before the argument name: **def f(mut x: Int)**. With no convention, an argument is a read-only borrow: a view into a value you don't own. **mut** makes it a writable view. ```mojo var i = 5 # Int, machine width (default) var i32: Int32 = 5 # SIMD[DType.int32, 1] ``` Literals are produced by the lexer, not built by a constructor. Each compile-time type (**IntLiteral**, **FloatLiteral**, **StringLiteral**) materializes into a runtime value. By default, integer literals are **Int**, floats are **Float64**, and strings are **String**. Use type annotations for specific types like **Byte** (**UInt8**), **Int16**, or **BFloat16**. Trivial register types (**Int**, **Float64**, **SIMD**) are **ImplicitlyCopyable** with no destructor. A copy is a register copy; **^** is a no-op (the compiler warns transfer has no effect); there's nothing to destroy. The rules still apply, they just compile to register moves or nothing. Values aren't mutable or immutable. Access is. | Name | Meaning | |------|----------------------------------------------| | var | always mutable | | ref | inherits the mutability of what it refers to | | Method | Meaning | |-----------------------------------------------|-----------| | \_\_init\_\_(out self, …) | construct | | \_\_init\_\_(out self, \*, copy: Self) | copy | | \_\_init\_\_(out self, \*, deinit move: Self) | move | | \_\_deinit\_\_(deinit self) | destroy | Copy, move, and destructors can't raise. The var assignment table shows which one each assignment runs. ```mojo var data: List[String] = ["a", "b", "c", "d", "e"] var s = data[1:3] # a Span view, no copy: [b, c] s[0] = "X" # writes through: data is [a, X, c, d, e] var text = "Hello, World!" var hi = text[codepoint=0:5] # a StringSpan view: "Hello" ``` A view is a non-owning window into a buffer someone else owns. **Span** views contiguous elements; **StringSpan** views UTF-8 text. Like **ref**, a view carries an origin, so the compiler keeps the source alive and tracks whether the view stays valid. | You write | What it does | |---------------------|----------------------------------------| | return x | copy out (when **ImplicitlyCopyable**) | | return x.copy() | copy out | | return x^ | transfer out | | -> T | return a value | | -> ref[origin] T | return a reference | Like a var assignment, **return x** copies; when **x** is at its last use the compiler moves it instead (you own it, so it can be moved). No **-> T^**: the **^** goes on the returned value in **return x^**, not on the return type. --- ## Mojo traits cheat sheet Sheet, Panel, } from '@site/src/components/CheatSheets/sheet'; {/* markdownlint-disable MD033 MD034 */} ## Lifecycle no requirements | Signature | Remark | |------------------------------------|-------------------| | `__deinit__(deinit self, /)` | provided | | `comptime __del__is_trivial: Bool` | compile-time flag | Automatically added to every eligible type (all fields are also **Deinitable**). When the type is trivial, Mojo skips the destructor. | Signature | Remark | |--------------------------------------------|-------------------| | `__init__(out self, *, deinit move: Self)` | provided | | `comptime __move_ctor_is_trivial: Bool` | compile-time flag | Required to store a type in **List**, **Optional**, or **Variant**, or to return it by move. **Refines:** Movable | Signature | Remark | |-----------------------------------------|-------------------| | `__init__(out self, *, copy: Self)` | provided | | `copy(self) -> Self` | provided | | `comptime __copy_ctor_is_trivial: Bool` | compile-time flag | The copy constructor is synthesized if all fields are **Copyable**. **Refines:** Copyable < Movable · no new requirements Can mask logical errors and hide code reasoning. Prefer **Movable** or **Copyable**. | Signature | Remark | |----------------------|--------| | `__init__(out self)` | | Reach for this when you need parameterized default-construction without arguments. **Refines:** Movable · no requirements (marker) No stable address: you can't take the address of **self** in imm-convention methods. **Identifiable** is meaningless for these types. Moved trivially; all fields must also conform. **Refines:** ImplicitlyCopyable < Copyable < Movable, Deinitable, RegisterPassable · no requirements (marker) A type whose values are treated as basic bit patterns. No constructors or destructors needed. All fields must also conform. ## Format | Signature | Remark | |-------------------------------------------------|----------| | `write_to(self, mut writer: Some[Writer])` | provided | | `write_repr_to(self, mut writer: Some[Writer])` | provided | If all fields conform, you inherit both methods through reflection. String, FileHandle, FileDescriptor conform | Signature | Remark | |----------------------------------------------|----------| | `write_string(mut self, string: StringSpan)` | | | `write[*Ts: Writable](mut self, *args: *Ts)` | provided | Use for loggers, network streams, string builders, etc. ## Testing **Refines:** Deinitable, Movable | Signature | Remark | |------------------------------------------------------|-----------------| | `Value: Copyable & Deinitable` | associated type | | `value(mut self, mut rng: Rng) raises -> Self.Value` | | Allows strategies to carry and advance state between draws. **value()** draws one sample from the random number generator. ## Accelerator traits | Signature | Remark | |---------------------------------------|--------------------| | `comptime device_type: AnyType` | the on-device type | | `_to_device_type(self, mut enc, ...)` | DeviceContext hook | A host type implements this so it can be handed to a GPU or other accelerator. **DeviceContext** calls the conversion hook to turn host into device at kernel launch. | Signature | Remark | |------------------------------------|-----------------------------------| | `target() -> _TargetType` | device target | | `encode_device_ptr(mut self, ...)` | required | | `encode[T](mut self, value, dst)` | provided (+ fields, tuple, array) | Encodes a value's fields into the accelerator's data layout. ## Compare & hash | Signature | Remark | |-------------------------------------|----------| | `__eq__(self, other: Self) -> Bool` | provided | | `__ne__(self, other: Self) -> Bool` | provided | Don't use with floating-point values (use **isclose()**). **NaN != NaN**. Mojo provides a fieldwise default. Override for caches, internal metadata, and custom behavior. **Refines:** Equatable | Signature | Remark | |-----------------------------------|----------| | `__lt__(self, rhs: Self) -> Bool` | | | `__gt__(self, rhs: Self) -> Bool` | provided | | `__le__(self, rhs: Self) -> Bool` | provided | | `__ge__(self, rhs: Self) -> Bool` | provided | Implement **\_\_lt\_\_()** unless it's expensive. If so, override all four. KeyElement = Hashable + Equatable + Movable | Signature | Remark | |--------------------------------------------|----------| | `__hash__(self, mut hasher: Some[Hasher])` | provided | | Signature | Remark | |-----------------------------------------------------|--------| | `__init__(out self)` | | | `_update_with_bytes(mut self, data: Span[Byte, _])` | | | `_update_with_simd(mut self, value: SIMD[_,_])` | | | `update(mut self, value: Some[Hashable])` | | | `finish(var self) -> UInt64` | | Hashers remain alive after finalization. All three update methods are required. | Signature | Remark | |--------------------------------------|----------| | `__is__(self, rhs: Self) -> Bool` | | | `__isnot__(self, rhs: Self) -> Bool` | provided | Excludes register-passable types, which don't have stable addresses. ## Convert | Signature | Remark | |-------------------------------------|------------------| | `__bool__(self) -> Bool` | Boolable | | `__int__(self) -> Int` | Intable | | `__int__(self) raises -> Int` | IntableRaising | | `__float__(self) -> Float64` | Floatable | | `__float__(self) raises -> Float64` | FloatableRaising | If the method raises, use the Raising variant. Boolable unlocks if / while / and / or usage. ## Math | Signature | Remark | |-----------------------------------------|------------------------------------| | `__abs__(self) -> Self` | **Absable** · abs() | | `__pow__(self, exp: Self) -> Self` | **Powable** · pow(), `**` | | `__round__(self) -> Self` | **Roundable** · round() | | `__round__(self, ndigits: Int) -> Self` | **Roundable** · round(), precision | | Signature | Remark | |---------------------------|-------------------------| | `__ceil__(self) -> Self` | **Ceilable** · ceil() | | `__floor__(self) -> Self` | **Floorable** · floor() | | `__trunc__(self) -> Self` | **Truncable** · trunc() | | Signature | Remark | |-------------------------------------------------------|--------------------| | `__ceildiv__(self, denominator: Self) -> Self` | CeilDivable | | `__ceildiv__(self, denominator: Self) raises -> Self` | CeilDivableRaising | **Refines:** ImplicitlyCopyable < Copyable < Movable | Signature | Remark | |------------------------------------------------------------|--------| | `__divmod__(self, denominator: Self) -> Tuple[Self, Self]` | | Math outlier. The tuple is (quotient, remainder). ## Iterate | Signature | Remark | |-------------------------------|--------------| | `__len__(self) -> Int` | Sized | | `__len__(self) raises -> Int` | SizedRaising | | Signature | Remark | |---------------------------------------------------------------------------------------------|-----------------| | `IteratorType[iterable_mut: Bool, //, iterable_origin: Origin[mut=iterable_mut]]: Iterator` | associated type | | `__iter__(ref self) -> Self.IteratorType[origin_of(self)]` | | Parameterized on mutability and origin. Yields references tied to the source lifetime. | Signature | Remark | |------------------------------------------------|-----------------| | `IteratorOwnedType: Iterator` | associated type | | `__iter__(var self) -> Self.IteratorOwnedType` | | No origin tracking. **Refines:** Deinitable, Movable | Signature | Remark | |-----------------------------------------------------------|-----------------| | `Element: Movable` | associated type | | `__next__(mut self) raises StopIteration -> Self.Element` | | | `bounds(self) -> Tuple[Int, Optional[Int]]` | provided | | `nth(var self, n: Int) -> Optional[Self.Element]` | provided | Requires an **Iterable** on the collection, and **Iterator** on the iterator. Don't rely on **bounds()** for safety checks. It's a hint. Typed raises (**StopIteration**). ## Interop Path conforms | Signature | Remark | |------------------------------|--------| | `__fspath__(self) -> String` | | **Refines:** Deinitable | Signature | Remark | |-----------------------------------------------------|--------| | `to_python_object(var self) raises -> PythonObject` | | **Refines:** Copyable < Movable, Deinitable | Signature | Remark | |--------------------------------------------------|--------| | `__init__(out self, *, py: PythonObject) raises` | | --- ## Mojo types & literals cheat sheet Sheet, Panel, } from '@site/src/components/CheatSheets/sheet'; {/* markdownlint-disable MD033 MD034 */} ```mojo # Every fixed-width number is a 1-lane SIMD # Float32 = Scalar[DType.float32] # = SIMD[DType.float32, 1] var v = SIMD[DType.float32, 4](1.0, 2.0, 3.0, 4.0) var d = v * 2.0 # [2, 4, 6, 8], all lanes v[0] = 5.0 # write one lane print(v.reduce_add()) # sum of lanes (14.0) ``` Width must be a power of two and is part of the type; its parameter type is **SIMDLength**. ```mojo SIMD[DType.float32, 4] # DType picks the lane type Scalar[DType.int] # == Int ``` Names mirror the types: **DType.float32** ↔ **Float32**, **DType.int8** ↔ **Int8**, **DType.bool** ↔ **Bool**. A **DType** is a name, not a type. It parameterizes **SIMD**, which stores the data. ```mojo var n = 42 # Int: machine width var u: UInt = 42 # machine width var small: UInt8 = 255 var big: Int64 = -9_000_000_000 ``` | Type | Meaning | |-----------------|---------------------------------| | Int / UInt | machine word (typically 64-bit) | | Int8 … Int256 | sized signed | | UInt8 … UInt256 | sized unsigned | | Byte | alias for UInt8 | Use **Int** for counts and indices; sized types when bit width is part of the contract. Each is an alias for a 1-lane SIMD. | Type | Meaning | |-----------------|---------------------------| | Float64 | IEEE double (default) | | Float32 | IEEE single | | Float16 | IEEE half | | BFloat16 | brain float (ML training) | | Float8_e4m3fn … | 8-bit (GPU, ML) | | Float4_e2m1fn | 4-bit (Blackwell+) | No bare **Float** type. Each is an alias for a 1-lane SIMD. | Name | Meaning | |-----------------------|------------------------------------------| | `bit_width_of[Int]()` | 64 on most platforms (from std.sys.info) | | UInt8.MAX | 255 | | Int8.MIN | -128 | | Float32.MAX_FINITE | largest finite | | Float32.MAX | may be inf | IEEE floats carry **inf**, **-inf**, **nan**, **-0.0**. ```mojo var i = 42 var f = Float64(i) # Int -> Float64 var s = Int8(i) # Int -> Int8 var back = Int(Int64(i)) # round trip # between SIMD-based types: .cast[] var g = f.cast[DType.int32]() ``` Variables never convert implicitly; the compiler enforces it. Literals convert only when it can prove the result is exact. | Literal | Meaning | |----------------------|---------------------------------------------------| | 42 | decimal Int | | 0xFF 0o52 0b1010 | hex, octal, binary | | 1_000_000 | underscores group digits | | 3.14 .5 2. 2.5e-3 | floats | | 2 ** 200 | comptime IntLiteral, comptime arbitrary precision | Leading zeros on base-10 integers are rejected. At runtime literals materialize to **Int** / **Float64**. ```mojo [1, 2, 3] # Array, length in the type {"id": 1, "qty": 9} # Dict (1, "a", 2.0) # Tuple, mixed types ``` Unannotated, a bracket literal defaults to **Array**. It adapts to the type you ask for: **var x: List[Int] = [1, 2, 3]**. ```mojo "double" 'single' # triple quotes: newlines and indentation included """line one line two""" r"C:\raw\path" # raw: no escape processing "\u20AC" # lowercase \u, 4 digits: € (EURO) "\U0001F44B" # uppercase \U, 8 digits: 👋 (above U+FFFF) # adjacent literals join, same line or across lines: "Hello" " world!" # -> "Hello world!" "Content of line 1. " "Content of line 2." ``` Escapes: | Escape | Meaning | |------------|------------------------| | \n \t | newline, tab | | \\" \\\\ | quote, backslash | | \xHH | byte (2 hex digits) | | \uHHHH | Unicode (4 hex digits) | | \UHHHHHHHH | Unicode (8 hex digits) | Source is UTF-8. **\u** and **\U** reject surrogate code points (U+D800 to U+DFFF); code points above U+FFFF need **\U**, not a surrogate pair. ```mojo var who = "Mojo" t"Hi, {who}!" # interpolation t"sum = {1 + 2}" # any expression t"{{literal braces}}" # -> {literal braces} rt"raw\path {who}" # raw t-string: \ literal, still interpolates String(t"x = {who}") # cast to String ``` Interpolations evaluate at runtime. | Name | Meaning | |------------|-------------------------------| | True False | boolean values | | None | the only NoneType value | | Self | the enclosing type | | _ | discard a value in assignment | | ... | marks a required trait method | - **Int width is platform-dependent** — use Int64 for a fixed width. - **Integer overflow wraps** — Int8(127) + 1 is -128. - **Float-to-int truncates toward zero** — Int(Float64(3.9)) is 3. - **Float8 needs a GPU** — no runtime CPU arithmetic. - **Int128 / Int256 are software-emulated.** - No implicit numeric conversion: `Int + Float64` is an error. Cast with `Float64(n)`, `Int(x)`. - Numbers are fixed-width SIMD scalars, not arbitrary-precision `int`; only a **comptime** **IntLiteral** is unbounded. - No bare `float` or `int` — pick **Int**, **Float64**, or a sized type. - Every scalar is a 1-lane **SIMD**; vectorizing widens the lane count, not a new type. - Integer overflow **wraps** (defined), not C++ undefined behavior or a Rust debug panic. - **DType** is a value-level tag that parameterizes **SIMD**, not a type alias. - **Int** is C++ `ssize_t` / Rust `isize`, not C++ `int`, which is usually 32-bit. --- ## Mojo closure declarations reference A *closure* is a nested function with a *capture list* that controls how it accesses values from the scope it's nested in: ```mojo def main(): var multiplier = 3 def scale(x: Int) {imm multiplier} -> Int: return x * multiplier print(scale(5)) # 15 ``` `{imm multiplier}` references `multiplier` from the enclosing scope as an immutable reference. Without the capture list, referencing any outer value is a compile error. A closure can't outlive the scope where it's declared. Mojo doesn't support escaping closures or async execution. :::note A closure is Copyable when every value it captures is Copyable. A closure can be copied manually into heap-allocated memory, but Mojo doesn't provide a built-in mechanism for heap-allocated or existential closures. ::: ## Closure syntax ```text def name(argument-list) {capture-list} -> ReturnType: body def name[parameter-list](argument-list) {capture-list} -> ReturnType: body def name(argument-list) raises {capture-list} -> ReturnType: body ``` Effects (for example, `raises`) go between the argument list and the capture list. The capture list appears immediately before the return arrow. It can be empty (`{}`) or omitted entirely; both forms prohibit references to outer values. The argument list, parameter list, effects, return type, and `where` clauses follow the same rules as top-level functions. See [Function declarations](./function-declarations.mdx). ## Capture list grammar A capture list is a brace-enclosed, comma-separated sequence of *entries*: | Form | Meaning | |----------------|-----------------------------------------------| | ` name` | Capture `name` with convention `` | | `` | Default convention for all free variables | | `name` | Capture `name` with convention `imm` | | ` name^` | Move-capture (only with `var` or no ``) | `` is one of `imm`, `mut`, `var`, or `ref`. Position within the list isn't significant: `{mut, var z}` and `{var z, mut}` are equivalent. Trailing commas are accepted. At most one entry can omit a name (the default-convention entry). A second unnamed entry produces an error, naming the default capture convention duplication. You may only use the `^` marker on `var` entries or entries with no convention keyword. ## Capture conventions | Convention | Form | Storage in closure | Lifetime tie to outer | |------------|--------------------------|-----------------------------------|-----------------------| | `imm` | `{imm name}` / `{imm}` | Immutable reference | Live | | `mut` | `{mut name}` / `{mut}` | Mutable reference | Live | | `ref` | `{ref name}` / `{ref}` | Reference, mutability from origin | Live | | `var` | `{var name}` / `{var}` | Owned copy | Independent | | Move | `{var name^}` | Owned, consumed from outer | Consumes outer | | Copyable | `{var^}` | Owned, closure is `Copyable` | Independent | ## `imm` Immutable reference. The default convention. The closure reads the outer value each time it's called, rather than capturing a copy: ```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 ``` `{imm}` with no variable name applies `imm` to every free variable in the body. A bare name without a convention keyword also defaults to `imm`: `{x}` is equivalent to `{imm x}`. ## `mut` Mutable reference. Writing to a captured value inside the closure modifies its binding in the outer scope: ```mojo def main(): var total = 0 def accumulate(x: Int) {mut total}: total += x accumulate(10) accumulate(20) print(total) # 30 ``` `{mut}` with no variable name applies `mut` to every free variable in the body, capturing each one by mutable reference. ## `var` Owned copy. The closure receives its own copy of the value when the closure is declared. Later changes to the outer binding don't affect the closure's copy, and vice versa: ```mojo def main(): var snapshot = 42 def frozen() {var snapshot} -> Int: return snapshot snapshot = 999 print(frozen()) # 42 ``` `{var}` with no variable name copies every free variable referenced in the closure body. The copy initializer runs once per closure declaration. Capturing a large `List` or `String` by `var` allocates at that point. Use `imm` or `mut` when an independent copy isn't needed. ## Move capture: `var name^` Transfers ownership of `name` into the closure. The outer binding is consumed; using it after the closure declaration is a compile error: ```mojo def main(): var data: List[Int] = [1, 2, 3] def take_data() {var data^}: print(data) take_data() # [1, 2, 3] # print(data) # error: 'data' is uninitialized # after move ``` Move capture skips the copy that `var name` would perform and is the only way to capture a move-only type by value. Constraints: - Only legal after `var` or after a bare name with no convention. - `{imm name^}`, `{mut name^}`, and `{ref name^}` are rejected. - A bare `name^` is equivalent to `var name^`. ## Copyable closures: `var^` `{var^}` with no variable name makes move capture the default for every free variable in the body. When every captured type is `Copyable`, the resulting closure value is also `Copyable`: ```mojo def main(): var label = "sensor-1" def tag() {var^} -> String: return label var clone = tag # closure value copied print(tag()) # sensor-1 print(clone()) # sensor-1 ``` Copying the closure invokes the copy initializer of each captured value. The copy happens at the assignment, not at the closure declaration. Constraints: - `{var^}` is a default-convention entry. A capture list can contain at most one default-convention entry. - If any captured type is move-only, the closure is `Movable` but not `Copyable`. Comparison with `{var name^}`: | Form | Captured names | Closure value | |---------------|-------------------------------|---------------------------------------| | `{var name^}` | Only `name`, by move | Not `Copyable` by default | | `{var^}` | All referenced names, by move | `Copyable` if captures are `Copyable` | ## `ref` Reference whose mutability comes from the outer binding's origin. The closure doesn't choose `imm` or `mut`; it preserves the mutability of that origin: ```mojo def show_mutability(ref items: List[Int]): def report() {ref items}: comptime if origin_of(items).mut: print("mut") else: print("immut") report() # `xs` uses default `imm` convention, immutable reference def from_imm(xs: List[Int]): show_mutability(xs) # `xs` uses `mut` convention, mutable reference def from_mut(mut xs: List[Int]): show_mutability(xs) def main(): var nums: List[Int] = [10, 20, 30] from_imm(nums) # immut from_mut(nums) # mut ``` `ref` is the only convention that forwards origin information unchanged. `imm` and `mut` create references with fixed mutability; `var` removes the origin relationship entirely. `ref` captures are intended for parameterized code that must work with different mutability contexts. In ordinary closures, `imm` and `mut` produce clearer signatures. ## Empty and omitted capture lists An empty capture list (`{}`) has the same result as omitting the capture list: any reference to an outer value is rejected with an error about inferring the capture convention. Both forms allow a body that uses only its arguments and locally declared values. The function behaves as a plain nested function without captures. Prefer `{}` when the absence of captures is intentional. The explicit braces make the constraint visible at the declaration. ## Mixing conventions Each captured value can use its own convention: ```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 declaration time) ``` A bare name in a mixed list uses `imm`, not the convention of surrounding entries: ```mojo # y is captured as 'imm', not 'mut' def f() {var z, mut x, y}: # ... ``` ## Default convention A convention keyword without a name sets the default for every free variable not explicitly named: ```mojo def main(): var a = 1 var b = 2 var z = "snapshot" def mixed() {mut, var z}: a += 10 # 'a' uses default: mut b += 20 # 'b' uses default: mut print(a, b, z) mixed() # 11 22 snapshot z = "changed" mixed() # 21 42 snapshot # ('z' was copied at declaration) ``` Rules: - A capture list can contain at most one default-convention entry. - Position within the list isn't significant. - Trailing commas are accepted. - The default doesn't apply to names covered by an explicit entry. In `{mut, var z}`, `var z` overrides the default for `z`. ## Parametric closures A closure can declare its own compile-time parameter list: ```mojo def main(): # The `Intable` trait supports `Int` conversion def double[T: Intable](x: T) {} -> Int: return Int(x) * 2 print(double[Int](5)) # 10 print(double[Float64](3.4)) # 6 ``` The parameter list, capture list, effects, and return type appear in the same order as on top-level functions: `name[parameters](arguments) effects {captures} -> ReturnType`. Parameters and captures work independently. Parameters are supplied at each call site, while the capture list controls the closure's relationship to the enclosing scope. Variadic parameters are also supported (`def closure[*Ts: Coord](*args: *Ts)`). ## Effects Effects appear between the argument list and the capture list. | Effect | Form | Type example | |-----------------|---------------------------------|----------------------------------------| | `raises` | `(args) raises {captures} -> T` | def (String) raises -> Int | | `thin` | `(args) thin -> T` | def (T) thin -> U | | `abi(language)` | `(args) abi(language) -> T` | def (Float64) thin abi("C") -> Float64 | :::caution You may not combine non-Mojo `abi()` effects with raising functions. Raising functions change calling conventions in a non-obvious ways. The Mojo compiler: - accepts `def (String) abi("Mojo") raises` - rejects `def (String) abi("C") raises` ::: `raises` example: ```mojo def main() raises: var y = 2 def divide(x: Int) raises {var y} -> Int: if y == 0: raise Error("divide by zero") return x // y print(divide(10)) # 5 ``` The `thin` effect applies to function *types*, not to closure declarations. `thin` describes a non-capturing function type, so it can't represent a closure that captures. A `thin` function type is also the only one that accepts trailing `where` clauses. See [Function declarations](./function-declarations.mdx). ## Nesting Closures can nest inside closures. Each level has its own capture list. An inner closure can capture a name already captured by its enclosing closure: ```mojo def main(): var y = 4 def outer() {var y} -> Int: def inner() {var y} -> Int: return y return inner() + y print(outer()) # 8 ``` An inner closure can capture an outer closure by name. This is how nested callbacks compose: ```mojo def main(): def make_adder(n: Int): def add(x: Int) {var n} -> Int: return x + n def twice(x: Int) {var add} -> Int: return add(add(x)) print(twice(5)) # ((5 + 3) + 3) = 11 # add(add(5)) = add(8) = 11 make_adder(3) ``` Closures are values and can be used in capture lists. An inner closure captures an outer closure by `var`, `imm`, `mut`, or `ref`, just like any other value. ## Capture-list errors | Compiler complaint | Trigger | |-----------------------------------------------------------------------------------|------------------------------------------------------------------------------| | Transfer sigil `^` without `var` convention | `^` after `mut`, `imm`, or `ref` | | Duplicate default convention | Two bare convention keywords in one list | | Unrecognized token in capture position | Token that isn't a convention keyword or name | | Missing comma between entries | Identifier followed by an unrecognized token | | Unterminated capture list | Missing closing `}` | | Outer name not covered by capture list | Body references an outer name the capture list doesn't cover | | Use after move capture | Reference to a name after `{var name^}` consumed it | ## Restrictions - **No escape.** A closure can't outlive its enclosing scope. Returning a closure from its declaring function or storing it past the enclosing scope's end isn't supported. - **No `thin` on declarations.** These apply to function types, not closure declarations. A declaration with captures can't be `thin`. - **Trait conformance with closure fields.** A struct can contain a closure-typed field and conform to a trait, but every method of that trait must be declared `capturing` until the capturing effect is removed (see `unified_closure_structs.mojo`). --- ## Mojo compound statements reference A *compound statement* has a header and a body. The header ends with `:` and is followed by an indented block with the body. The body can contain simple statements, other compound statements, or both. ```mojo if condition: # Header do_something() # Body ``` The body must be indented more than the header. The first body statement sets the indentation for the rest of the body: ```mojo if condition: do_something() do_more() # Error because statement has excess indentation ``` ## If statements An `if` statement executes a block conditionally: ```mojo if x > 0: print("positive") elif x < 0: print("negative") elif x == 0: print("zero") else: print("you should never get here") ``` Conditions are evaluated in order. Add as many as needed. The first true condition runs its block, and the statement exits. The `else` block runs if no condition is true. When the body is a single simple statement, you can write it on a single line, although many style guides discourage this. ```mojo if x > 0: print("positive") ``` Common shortcuts from other languages won't work in Mojo: ```mojo x > 0 and print("positive") # Error because 'None' isn't truthy print("positive") if x > 0 else pass # Error because 'pass' isn't an expression ``` ## While loops The `while` loop repeats its body while a condition is true: ```mojo var count = 0 while count < 10: print(count) count += 1 ``` Use `break` to exit the loop early and `continue` to skip to the next iteration: ```mojo while True: var item = get_next() if item is None: break # Exit loop if no more items if not is_valid(item): continue # Skip invalid items process(item) # Only runs for valid items ``` ## For loops The `for` statement iterates over a sequence: ```mojo for item in items: process(item) for i in range(10): # [0, 10) print(i) ``` To support iteration, a sequence must implement `__iter__()` and `__next__()`. A `for` loop desugars to a `while` loop that uses these methods. Destructuring works directly in the loop target. This lets you unpack tuple elements as you iterate. In this example, each item in `pairs` is unpacked into `key` and `value` for every iteration: ```mojo for key, value in pairs: # For example, [("a", 1), ("b", 2), ...] print(key, value) ``` ### Loop variable bindings Use `var` and `ref` conventions to control ownership, copying, and mutability behavior in loop variables. By default, loop variables are immutable references to the iterated items (`imm`). To create a mutable copy, use `var`. To maintain value mutability, use `ref`: ```mojo var list: List[String] = ["a", "b", "c", "d"] for var item in list: item = item + "x" # works. item is mutable copy of list element print(item) # prints "ax", then "bx", "cx", and "dx" print(list) # unchanged for ref item in list: item = item + "x" # mutability picked up in reference to list element print(item) # prints "ax", then "bx", "cx", and "dx" print(list) # changed to ["ax", "bx", "cx", "dx"] ``` ## Loops and else clauses An optional `else` clause runs when the loop exits normally. It does not run if the loop exits with `break`: ```mojo var found = False for item in items: if item == target: found = True break else: print("not found") # Only runs if break was never hit ``` Both `for` and `while` loops support `else`. ## Error handling A `try` statement executes code that may raise errors. ```mojo var result: Bool try: result = risky() except e: handle(e) ``` ### Structure Each `try` statement requires at least one `except` or `finally` clause: ```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 ``` Execution proceeds in a fixed order: 1. The `try` block runs first. 1. If an error occurs, the matching `except` block runs. 1. If no error occurs, the `else` block (if present) runs after the `try` block. 1. If included, a `finally` block always runs last. ### Error binding Bind the error to a name with `except name`: ```mojo try: risky() except e: print(e) # e is the caught error ``` Without a binding, the error is caught but not accessible. There is no default error variable. This is useful when you want to respond to an error state without needing the error details: ```mojo try: risky() except: print("something went wrong") ``` ### Typed errors When a function declares a specific error type with `raises ErrorType`, the bound variable's type is inferred: ```mojo @fieldwise_init struct NetworkError: var message: String var code: Int def fetch() raises NetworkError -> String: raise NetworkError("HTCPCP", 418) # See RFC 2324 * try: var result = fetch() except e: # e is inferred as `NetworkError` print(e.message) # Known types support direct field access print(e.code) # `.code` and `.message` only work because `e` # is a known to be `NetworkError` ``` A `try` block handles one error type. The compiler raises an error if code in the `try` block can raise more than one error type. :::note [RFC 2324](https://datatracker.ietf.org/doc/html/rfc2324) defines the Hyper Text Coffee Pot Control Protocol (HTCPCP) as an April Fools' joke. ::: ## Context managers A `with` statement manages resources using context managers. *Context managers* define setup (`__enter__`) and cleanup (`__exit__`) operations. The cleanup always runs when the block exits, even if an error occurs: ```mojo with open("file.txt") as f: var content = f.read() # File is closed here, even if an error occurred ``` Multiple context managers can share a single `with` statement: ```mojo with open("input.txt") as f_in, open("output.txt", "w") as f_out: f_out.write(f_in.read()) ``` This is equivalent to nested `with` statements. ### How context managers work When a `with` block is entered, `__enter__()` is called on the context manager expression. The result is bound to the `as` target if present. A context manager that defines only `__enter__()` is valid; `__exit__()` is optional. When the block exits, `__exit__()` is called if it exists, even if an error occurs. A minimal custom context manager: ```mojo struct Scope(ImplicitlyCopyable): var label: String def __init__(out self, label: String): self.label = label def __enter__(self) -> Self: # perform setup tasks print("entering", self.label) return self def __exit__(self): # perform cleanup tasks print("exiting", self.label) def main(): with Scope("setup") as s: print("inside", s.label) # entering setup # inside setup # exiting setup ``` `__enter__` returns `self` so the `as` target binds to the manager. The `ImplicitlyCopyable` conformance lets the compiler return `self` by value. ## Compile-time control flow `comptime if` and `comptime for` run at compile time. The condition or sequence must be a compile-time value or expression. Use them to generate code based on compile-time conditions. You cannot use runtime values in `comptime` statements. ### comptime if `comptime if` selects a branch at compile time, pruning the unselected branches. Only the selected branch appears in the compiled program. ```mojo from std.sys import size_of comptime if size_of[Int]() == 8: print("64-bit") else: print("Probably 32-bit") ``` The condition must be a compile-time expression. In this example, `runtime_value` is not available at compile time, so the code errors during compilation: ```mojo comptime if runtime_value > 0: # Error because 'comptime if' requires pass # compile-time evaluation ``` `comptime if` supports `elif` and `else` like the regular `if` statement. ### comptime for `comptime for` unrolls a loop at compile time. Each iteration is compiled as separate code. This creates a bigger binary but improves runtime performance by eliminating loop overhead and enabling further optimizations. ```mojo comptime for i in range(3): print(i) # Compiled as: print(0); print(1); print(2) ``` Use `comptime for` to generate repeated code patterns or iterate over compile-time sequences. ## Scopes Each compound statement body creates a new scope. Variables declared inside a body are not visible outside it: ```mojo if condition: var x = 10 print(x) # Error: x is not in scope ``` `with` statement variables bound with `as` are scoped to the `with` block: ```mojo with open("file.txt") as f: var data = f.read() # f is not accessible here ``` Nested functions create their own scope and can capture variables from enclosing functions with capture lists: ```mojo def outer(): var count = 0 def inner() {mut count}: # Capture count by mutable reference count += 1 # Updates captured count inner() print(count) # 1 ``` --- ## @align The `@align` decorator specifies a minimum memory alignment for values of a struct type. If you already work with low-level memory, SIMD, or GPUs, you can think of `@align` as a way to make alignment a property of the type itself, enforced by the compiler. If not, the short version is this: alignment controls where values are placed in memory, and some hardware requires or benefits from specific alignments. Most Mojo code doesn't need explicit alignment. You only need `@align` when your types need placement at specific boundaries, such as when interacting with GPU buffers, using SIMD instructions, or avoiding cache-line contention in concurrent code. Without `@align`, alignment requirements must be tracked manually and enforced at allocation sites. With @align, the requirement becomes part of the type, and the compiler ensures it's respected everywhere the type is used. ## What alignment means Every byte in memory has an address. An address is N-byte aligned if its address is evenly divisible by N. Examples: - 8-byte aligned addresses: 0, 8, 16, 24, etc. - 64-byte aligned addresses: 0, 64, 128, 192, etc. Hardware often loads memory in fixed-size chunks, such as cache lines. When a value begins at an aligned address, it fits cleanly within those chunks. When it doesn't, hardware may need extra memory accesses, or may reject the access entirely. Alignment affects where a value begins in memory, and it also affects how large the value is: Mojo rounds a struct's size up to a multiple of its alignment. ## Basic usage Add `@align(N)` to your struct definition, where `N` is a positive power of 2 and the number represents the _minimum_ required alignment in bytes: ```mojo from std.sys import align_of @align(64) struct CacheAligned: var data: Int def main(): print(align_of[CacheAligned]()) # Prints 64 ``` In this example, CacheAligned is aligned to 64 bytes, even though Int normally requires only 8-byte alignment. ## Determining alignment The actual alignment of a struct is the maximum of: - The value specified by @align(N), if present. - The struct's natural alignment (the maximum alignment of its fields). - The alignment requirements of any embedded aligned fields. You can't reduce alignment below the natural alignment of the struct. The `@align` decorator specifies a _minimum_, not an override: ```mojo from std.sys import align_of @align(4) struct TryToReduce: var x: Int # Int has 8-byte natural alignment def main(): print(align_of[TryToReduce]()) # Prints 8 ``` When a struct contains an aligned field, the outer struct inherits that alignment: ```mojo from std.sys import align_of @align(64) struct CacheAligned: var x: Int struct Container: var aligned: CacheAligned var other: Int def main(): print(align_of[Container]()) # Prints 64 ``` ## Stack and heap behavior Both stack and heap allocations respect `@align`: ```mojo from std.sys import align_of from std.memory import alloc, dealloc @fieldwise_init @align(64) struct CacheAligned: var data: Int def use_aligned(): # Stack allocation var stack_value = CacheAligned(42) # Heap allocation var heap_alloc = alloc[CacheAligned]({count = 1}) dealloc(heap_alloc^) ``` You don't need to manually request alignment when allocating values of an aligned type. ## Alignment and arrays The `@align` decorator guarantees alignment of the base address of a value, including the base pointer of an array. It pads the size of the struct to be a multiple of the alignment. Mojo lays out array elements using `size_of[T]()` which rounds up to a multiple of `align_of[T]()`: ```mojo from std.sys import align_of, size_of from std.memory import alloc, dealloc @align(64) struct CacheAligned: var data: Int # 8 bytes def demonstrate_array_stride(): var allocation = alloc[CacheAligned]({count = 4}) var arr = allocation.unsafe_ptr() print(align_of[CacheAligned]()) # 64 print(size_of[CacheAligned]()) # 64 # All elements of arr are guaranteed to be 64-byte aligned dealloc(allocation^) ``` ## Parameterized structs Alignment also works with parameterized structs. All instances of a parameterized type share the same alignment requirement. Because of this, under certain circumstances, you may find that the alignment isn't tuned by its types: ```mojo from std.sys import align_of @fieldwise_init @align(128) struct AlignedType[T: Copyable & Deinitable]: var value: Self.T def main(): print(align_of[AlignedType[Int8]]()) # 128 print(align_of[AlignedType[Int64]]()) # 128 ``` Compare this with an alignment of 4, where the maximum of the decorator value (`N`) and the type's natural alignment produces a different result: ```mojo from std.sys import align_of @fieldwise_init @align(4) struct AlignedType[T: Copyable & Deinitable]: var value: Self.T def main(): print(align_of[AlignedType[Int8]]()) # 4 print(align_of[AlignedType[Int64]]()) # 8 ``` ## Interaction with RegisterPassable When `@align` is present, single-field register-passable structs aren't flattened. This preserves the alignment requirement: ```mojo from std.sys import align_of, size_of @align(32) struct AlignedTrivial(RegisterPassable): var value: Int def main(): print(align_of[AlignedTrivial]()) # 32 print(size_of[AlignedTrivial]()) # 32 ``` ## Requirements and errors Mojo's `@align` decorator has the following requirements: - The alignment value must be a positive power of 2. - The maximum supported alignment is 2^29 bytes. - The value must be known at compile time. - The decorator requires exactly one argument. Invalid uses produce compile-time errors: ```mojo @align(0) struct Bad1: var x: Int @align(3) struct Bad2: var x: Int @align(1073741824) struct Bad3: var x: Int @align struct Bad4: var x: Int @align(64, 128) struct Bad5: var x: Int @align("64") struct Bad6: var x: Int ``` ## Special case: @align(1) Using @align(1) is valid and produces no warning. It doesn't reduce alignment below the natural alignment of the struct. This can be useful as a fallback value in parametric code: ```mojo from std.sys import align_of @align(1) struct MinimalAlign: var x: Int def main(): print(align_of[MinimalAlign]()) # Prints 8 ``` ## Real-world example: hardware descriptors Some hardware accelerators require aligned descriptors for correctness. For example, NVIDIA's Tensor Memory Accelerator requires 64-byte aligned descriptors. Before `@align`, this required explicit allocation tricks: ```mojo # Verbose and error-prone var tensormap = my_custom_stack_allocation[1, TensorMap, alignment=64]()[0] ``` With `@align`, the type encodes your alignment requirement: ```mojo @align(64) struct TensorMap: # Descriptor fields pass var tensormap = TensorMap() var heap_tensormap = alloc[TensorMap]({count = 1}) ``` Alignment is enforced automatically everywhere the type is used. Both stack and heap allocations respect `@align`. ## Parametric alignment The alignment value can also be a struct parameter, enabling parameterized aligned types: ```mojo from std.sys import align_of @align(Self.alignment) struct AlignedBuffer[alignment: Int]: var data: Int def main(): print(align_of[AlignedBuffer[64]]()) # Prints 64 print(align_of[AlignedBuffer[128]]()) # Prints 128 ``` The alignment is validated when the struct is instantiated, so invalid values like `AlignedBuffer[3]` will produce a compile-time error. --- ## @always_inline You can add the `@always_inline` decorator on any function to make the Mojo compiler "inline" the body of the function (copy it) directly into the body of the calling function. This eliminates potential performance costs associated with function calls jumping to a new point in code. Normally, the compiler will do this automatically where it can improve performance, but this decorator forces it to do so. The downside is that it can increase the binary size by duplicating the function at every call site. For example: ```mojo @always_inline def add(a: Int, b: Int) -> Int: return a + b print(add(1, 2)) ``` Because `add()` is decorated with `@always_inline`, Mojo compiles this program without adding the `add()` function to the call stack, and it instead performs the addition directly at the `print()` call site, as if it were written like this: ```mojo print(1 + 2) ``` ## `@always_inline("nodebug")` You can also use the decorator with the `"nodebug"` argument, which has the same effect to inline the function, but without debug information. This means that you can't step into the function when debugging. This decorator is intended to be used on the low-level functions in a library, which may wrap primitive functions, MLIR operations, or inline assembly. Marking these functions as "nodebug" prevents users from accidentally stepping into low-level non-Mojo code when debugging. ## `@always_inline("builtin")` The `"builtin"` argument is like `"nodebug"`, but even stricter. The `"builtin"` version of the decorator should only be used on functions that wrap a single MLIR operation that the compiler has special compile-time handling for. It allows the compiler to inline the function when it's used in a parameter context. :::caution Using this version of the decorator requires some knowledge of the Mojo compiler's internals. Using it outside of the standard library is not recommended. Use the standard `@always_inline` decorator or the `"nodebug"` version, instead. ::: This version of the decorator does everything that `"nodebug"` does, plus two other behaviors: - It checks the body of the function to validate that it doesn't use anything that `@always_inline("builtin")` can't handle. This checks that there is no control flow, no function calls to functions that are not themselves `@always_inline("builtin")`, no use of unsupported MLIR operations, etc. - When the function is used in a parameter context, it is unconditionally inlined. For more details and background, see [the `@always_inline("builtin") proposal](https://github.com/modular/modular/blob/mojo/v1.1.0/Mojo/proposals/always_inline_builtin.md). --- ## @__copy_capture :::caution Deprecated The `@__copy_capture` decorator is deprecated and will be removed in a future release. Use the current [closure](/docs/manual/functions/closures/) syntax with capture lists. ::: You can add the `@__copy_capture` decorator on a legacy closure to capture register-passable values by copy. This decorator causes a nested function to copy the value of the indicated variable into the closure object at the point of formation instead of capturing that variable by reference. This allows you to pass the closure as a parameter, but lifetimes aren't guaranteed to be respected. ```mojo def foo(x: Int): var z = x @__copy_capture(z) @__parameter def formatter() -> Int: return z z = 2 print(formatter()) def main(): foo(5) ``` --- ## @deprecated The `@deprecated` [decorator](/docs/reference/decorators/) marks a declaration as obsolete and scheduled for removal. It actively signals to callers that an API still works today but won't stick around forever. Deprecation lets you safely reshape your codebase. With it, you can refine designs, replace older patterns, and introduce better tooling without forcing sudden changes. When you mark something as deprecated, you give your users the time and information they need to move to newer APIs or refactor their code for the upcoming feature loss before the old API disappears. ## Deprecation information Deprecation doesn't prevent using symbols. Instead, it surfaces guidance in the form of compiler warnings. Mojo offers the `@deprecated` decorator with two styles: - **`@deprecated(use=symbol)`**: _Use this style when there's a clear successor to the previous symbol._ The compiler warns callers when they use the deprecated item and points them toward the symbol you recommend. This is a gentle nudge that says, "This call still works, but you should really start using the other thing instead." - The argument for `use` is the actual symbol. Don't quote it. - The symbol must be valid or the compiler will error. - **`@deprecated("message")`**: _Use this version when you want to explain the change in your own words._ If there's no direct replacement in play, the message style lets you explain the impact of your deprecation. The compiler displays the supplied string in its warning where the deprecated item is used. A message makes it easy to steer callers, give context, or note that the feature is going away entirely. :::note Deprecation practices When deprecating an API, consider: - **Clarity**: Explain the reason for deprecation when it provides actionable context for the user. - **Actionability**: When possible, point to a concrete replacement (`use`) or next step (message). - **Consistency**: Use the same phrasing across related APIs - **Precision**: When possible, deprecate individual functions or methods rather than entire types. Deprecation is most effective when it fits into clear, predictable upgrade paths. ::: ## How to deprecate The following sample demonstrates how to apply deprecation using both built-in styles: ```mojo # Mark function `a` as deprecated with a custom message @deprecated("Sunsetting a") def a(): pass # Mark function `b` as deprecated with alternative @deprecated(use=c) def b(): pass # `c` is `b`'s recommended replacement after deprecation def c(): pass def main(): a() # custom warning b() # warning with recommended replacement c() # no warning # Demonstrate that only warnings are issued print("This is a functioning app") ``` Output: ```text deprecation.mojo:16:6: warning: Sunsetting a a() # custom warning ~^~ deprecation.mojo:3:4: note: 'a' declared here def a(): ^ deprecation.mojo:17:6: warning: 'b' is deprecated, use 'c' instead b() # warning with recommended replacement ~^~ deprecation.mojo:8:4: note: 'b' declared here def b(): ^ This is a functioning app ``` ### Items you can deprecate In Mojo, you can deprecate any of the following items: - **Structs**: ```mojo @deprecated(use=PerformantStruct) struct LegacyStruct: # ... ``` - **Functions**: ```mojo @deprecated("This function is being phased out") def legacy_function(self): pass ``` - **Traits**: ```mojo @deprecated(use=Honkable) trait Quackable: def quack(self): ... ``` - **`comptime` values**: ```mojo @deprecated("Use tau instead") comptime pi = 3.141592 ``` --- ## @doc_hidden The `@doc_hidden` [decorator](/docs/reference/decorators/) marks a declaration as hidden from documentation generation. It allows you to exclude internal implementation details, special methods, or other code from appearing in published API documentation while keeping them accessible in your source code. This decorator is particularly useful when you need to maintain internal APIs, helper methods, or alternative initializers that exist for implementation purposes but shouldn't be part of your library's public documentation. API members with names starting and ending with double underscores ("dunder" members) are always treated as public and included in documentation unless they are decorated with `@doc_hidden`. Mojo treats any other API names starting with a single or double underscore (`_` or `__`) as internal and omits them from the generated documentation—no need for `@doc_hidden`. The `@doc_hidden` decorator only affects documentation generation. Hidden declarations are fully accessible in source code and can be accessed like any other declaration. The same is true of internal declarations that start with underscores—they are internal _by convention_, there are no access restrictions. ## When to use `@doc_hidden` Use `@doc_hidden` to hide: - **Alternative initializers and dunder methods**: Hide alternative `__init__()` methods or other dunder methods that users don't need to call directly. The `@doc_hidden` decorator is especially useful for dunder methods, since they're public by default and you can't hide them by renaming them. - **Internal methods**: Hide private or internal helper methods that are implementation details. - **Deprecated internals**: Hide old internal APIs that remain for backward compatibility but shouldn't appear in new documentation. See also [`@deprecated`](/docs/reference/decorators/deprecated/). ## Usage Apply `@doc_hidden` just above any declaration you want to exclude from generated documentation: ```mojo struct Calculator: """A simple calculator struct demonstrating @doc_hidden.""" var value: Int def __init__(out self, initial_value: Int = 0): """Creates a new Calculator with an initial value. Args: initial_value: The starting value for the calculator. Defaults to 0. """ self.value = initial_value @doc_hidden def __init__(out self): """Internal initializer that should not appear in public documentation. This initializer exists for implementation purposes but users should prefer the initializer that takes an initial value. """ self.value = 0 def add(mut self, amount: Int): """Adds a value to the calculator. Args: amount: The value to add. """ self.value += amount ``` The no-argument `__init__()` initializer in this example uses `@doc_hidden`. It works in code but won't show up in the generated documentation. ## What can be hidden You can apply `@doc_hidden` to most APIs: - **Functions and methods** Hide any function or method overload, including initializers (`__init__()`) and other special methods: ```mojo struct Point: @doc_hidden def __init__(out self): pass ``` The `@doc_hidden` decorator only hides the overload immediately following the decorator. So the same function can have both documented overloads and hidden overloads. - **Entire structs** Hide helper or internal structs. ```mojo @doc_hidden struct InternalHelper: pass ``` - `comptime` values and members ```mojo @doc_hidden comptime INTERNAL_CONSTANT = 42 ``` - **Struct fields** ```mojo struct PublicStruct: @doc_hidden var implementation_detail: Int pass ``` --- ## @export You can add the `@export` decorator on any function to make it publicly available as an exported symbol in the compiled artifact, allowing it to be called from external code. An `@export` function must declare its calling convention with an explicit [`abi`](/docs/reference/function-declarations#abi-c) effect. ```mojo # This function is internal - not an exported symbol def internal_helper(): print("Internal") # This function is exported under its own name, "my_exported_function" @export def my_exported_function() abi("Mojo"): print("Exported!") internal_helper() # This function is exported under the name "my_renamed_function" @export("my_renamed_function") def my_other_function() abi("Mojo"): print("Another function.") ``` The `@export` decorator can take an optional argument: - An alternate name to export the function under, as shown above. Use the name specifier and `abi("C")` effect to export a function that complies with the C calling conventions. You must also supply a function name that is a valid C identifier. For example: ```mojo @export("my_func") def my_function( name: StaticString, ptr: OpaquePointer[MutUntrackedOrigin], ) abi("C") -> None: pass ``` :::note Initialize the runtime in shared libraries If you compile an exported function into a shared library (`mojo build --emit shared-lib`) and call it from a non-Mojo host program such as C or C++, no Mojo `main()` function runs, so the Mojo runtime is never initialized. Call [`initialize_runtime()`](/docs/std/runtime/initialize_runtime/) before calling any other standard library functions. See [Call a Mojo shared library from C or C++](/docs/tools/compilation/#call-a-mojo-shared-library-from-c-or-c) for details. ::: To call Mojo from Python, register functions with a module builder. See [Calling Mojo from Python](/docs/manual/python/mojo-from-python/) for details. --- ## @fieldwise_init You can add the `@fieldwise_init` decorator on a struct to generate the field-wise `__init__()` initializer. For example, consider a simple struct like this: ```mojo @fieldwise_init struct MyPet: var name: String var age: Int ``` Mojo sees the `@fieldwise_init` decorator and synthesizes a field-wise initializer, the result being as if you had actually written this: ```mojo struct MyPet: var name: String var age: Int def __init__(out self, var name: String, age: Int): self.name = name^ self.age = age ``` You can synthesize the copy initializer and move initializer by adding the `Copyable` trait to your struct. For more information about these lifecycle methods, read [Life of a value](/docs/manual/lifecycle/life/). ## Implicit conversion Implicit conversion lets you pass a value and lets a type build itself, without calling the initializer directly. This keeps caller code simple and clean. You enable this by marking an initializer as [`@implicit`](/docs/reference/decorators/implicit/) or using `@fieldwise_init("implicit")` to create one for you. For example, if `MyStruct` has an initializer that accepts an `Int`, you can construct an instance like this: ```mojo var an_instance = MyStruct(42) ``` A function that takes a `MyStruct` will accept that instance, an explicit initializer call, or the value that can be converted into one: ```mojo some_function(an_instance) # pass an instance some_function(MyStruct(42)) # build one directly some_function(42) # implicit conversion ``` All three forms create a `MyStruct` for the call. Some may have small compile-time or run-time differences. ### Declaring implicit initialization You can declare implicit initializers in two ways: - Use `@fieldwise_init("implicit")` to auto-create one, as long as your type has exactly _one_ instance field. This limit applies to the type itself, not just to initializer arguments. - Add [`@implicit`](/docs/reference/decorators/implicit/) to an initializer you write. The initializer can accept only one argument. :::note Read more about [initializers and implicit conversion](/docs/manual/lifecycle/life/#constructors-and-implicit-conversion). ::: ### Fieldwise and implicit example Here is a type that stores an `Int`. It can be created with an integer or from any value that can be floored and converted to an integer. It uses `@fieldwise_init("implicit")` for integers and creates an `@implicit` initializer for other values: ```mojo from std.math import Floorable, floor # Creates an implicit initializer and limits the type to one instance field. @fieldwise_init("implicit") struct FlooringInt: var floored: Int # Allows implicit conversion from types that can be floored and made into an Int. @implicit def __init__[T: Floorable & Intable](out self, value: T): self.floored = Int(floor(value)) def floored(value: FlooringInt) -> Int: return value.floored def main(): print(floored(FlooringInt(42))) # pass an instance, output: 42 print(floored(2)) # pass Int, output: 2 print(floored(52.6)) # pass Float64, output: 52 var x = BFloat16(192.3) print(floored(x)) # pass BFloat16, output: 192 var y: FlooringInt = 180 print(y.floored) # output 180 var z: FlooringInt = 3.14159 print(z.floored) # output: 3 ``` What you don't see in this example is an initializer for integers. Adding `@fieldwise_init("implicit")` lets the compiler build it for you. If you wrote this by hand, it might look like this: ```mojo @implicit def __init__(out self, floored: Int): self.floored = floored ``` --- ## @implicit You can add the `@implicit` decorator on any single-argument initializer to identify it as eligible for implicit conversion. For example: ```mojo struct MyInt: var value: Int @implicit def __init__(out self, value: Int): self.value = value def __init__(out self, value: Float64): self.value = Int(value) ``` This implicit conversion initializer allows you to pass an `Int` to a function that takes a `MyInt` argument, or assign an `Int` to a variable of type `MyInt`. However, the initializer that takes a `Float64` value is **not** an implicit conversion initializer, so it must be invoked explicitly: ```mojo def func(n: MyInt): print("MyInt value: ", n.value) def main(): func(Int(42)) # Implicit conversion from Int: OK func(MyInt(Float64(4.2))) # Explicit conversion from Float64: OK func(Float64(4.2)) # Error: can't convert Float64 to MyInt ``` ## Deprecation Over time, you may decide that an implicit conversion is no longer appropriate for your code base. For example, it may hide complexity or cause ambiguous function overloads. In such cases, Mojo lets you mark a conversion as deprecated using its built-in `deprecated` argument on the @implicit decorator. Deprecation allows you to phase out the conversion gradually instead of causing abrupt behavior changes. Supply a Boolean value to the `deprecated` argument: ```mojo struct MyStruct: @implicit(deprecated=True) def __init__(out self, value: Int): # ... ``` This tells the compiler to emit a warning when the conversion is used implicitly, without breaking existing code: ```mojo _: MyStruct = 1 # Warns on implicit conversion _ = MyStruct(1) # No warning. Conversion is explicit ``` --- ## Mojo decorators A Mojo decorator modifies or extends the behavior of a struct, function, or other declaration at compile time. You place the decorator on the line above the declaration it applies to, prefixed with `@`. ```mojo @fieldwise_init struct Point: var x: Float64 var y: Float64 ``` After the `@`, a decorator is followed by a name, with optional arguments in parentheses. Each decorator goes on its own line. You can stack multiple decorators on a single declaration: ```mojo @fieldwise_init @align(64) struct CacheLine: var data: SIMD[DType.float32, 16] ``` Decorators apply bottom-up: the one closest to the declaration is applied first. :::note No custom decorators Mojo doesn't support custom decorators. The decorators in this section are built into the compiler. ::: ## Decorators The following pages describe each built-in decorator with examples. ## Decorator targets Not every decorator works on every declaration. This table shows which decorators are valid on which targets. | Decorator | `struct` | `def` | method | `trait` | `comptime` | `var` | field | |---------------------------|----------|-----------------|--------|---------|------------|-------|-------| | `@align` | yes | | | | | | | | `@always_inline` | | yes | yes | | | | | | `@extensibility.register` | yes | | | | | | | | `@__copy_capture` | | yes1 | | | | | | | `@deprecated` | yes | yes | yes | yes | yes | | | | `@doc_hidden` | yes | yes | yes | | yes | | yes | | `@export` | | yes | | | | | | | `@fieldwise_init` | yes | | | | | | | | `@implicit` | | | yes | | | | | | `@no_inline` | | yes | yes | | | | | | `@__parameter` | | yes1 | | | | | | | `@staticmethod` | | | yes | | | | | 1`@__copy_capture` and `@__parameter` work only on *nested* functions. --- ## @no_inline You can add the `@no_inline` decorator on any function to prevent it from being inlined by the compiler. ```mojo @no_inline def my_large_function(): ... ``` Inlining is an optimization that reduces function call overhead for small, frequently-called functions. Functions can be explicitly marked for inlining using [`@always_inline`](/docs/reference/decorators/always-inline/), or may be inlined automatically by the compiler. Too many inlined functions can slow compilation and substantially increase the binary size of the compiled program. In particular, large or complex functions may not benefit as much from inlining. --- ## @__parameter :::caution Deprecated The `@__parameter` decorator is deprecated and will be removed in a future release. Use the current [closure](/docs/manual/functions/closures/) syntax instead. The previous spelling `@parameter` is still accepted with a deprecation warning. ::: You can add `@__parameter` on a nested function to create a legacy capturing closure. This means you can create a closure function that captures values from the outer scope (regardless of whether they are variables or parameters), and then use that closure as a parameter. For example: ```mojo def use_closure[func: def(Int) capturing[_] -> Int](num: Int) -> Int: return func(num) def create_closure(): var x = 1 @__parameter def add(i: Int) -> Int: return x + i var y = use_closure[add](2) print(y) def main(): create_closure() ``` ```output 3 ``` Note the `[_]` in the function type: ```mojo def use_closure[func: def(Int) capturing[_] -> Int](num: Int) -> Int: ``` This origin specifier represents the set of origins for the values that the legacy closure captures. This allows the compiler to correctly extend the lifetimes of those values. For more information on lifetimes and origins, see [Lifetimes, origins and references](/docs/manual/values/lifetimes/). --- ## @staticmethod You can add the `@staticmethod` decorator on a struct method to declare a static method. For example: ```mojo from std.pathlib import Path struct MyStruct(Movable): var data: List[UInt8] def __init__(out self): self.data = List[UInt8]() @staticmethod def load_from_file(file_path: Path) raises -> Self: var new_struct = MyStruct() new_struct.data = file_path.read_bytes() return new_struct ^ ``` Unlike an instance method, a static method doesn't take an implicit `self` argument. It's not attached to a specific instance of a struct, so it can't access instance data. For more information see the documentation on [static methods](/docs/manual/structs/#static-methods). --- ## Mojo docstring reference Mojo uses docstring literals to generate API reference documentation data, which can be processed to produce page content or read directly from source. Place docstrings immediately after declarations. Docstrings support Markdown, freeform text, labeled sections, and instructive code examples: ```mojo def greet(name: String) -> String: """Returns a greeting string for the given name. Produces a simple `"Hello, name!"` string suitable for display or logging. Args: name: The name to include in the greeting. Returns: A greeting of the form `"Hello, name!"`. """ return "Hello, " + name + "!" ``` Mojo docstrings follow conventions used by [Python docstrings](https://peps.python.org/pep-0257/) and the [Google docstring style guide](https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings). ## Placement Docstrings let you document declarations at the point they appear in the source file. | Declaration | Position | |---------------------|-------------------------------------------| | Function or method | After the signature, before the body | | Struct or trait | After the opening line, before members | | Field or `comptime` | After the declaration, not before it | | Module or Package | First string in the file, after imports | Type, trait, and function docstrings use the same indentation level as the declaration. Field and `comptime` docstrings use the same indentation level as the field or `comptime` name. ```mojo struct Color: """Represents an RGB color.""" # Struct docstring var r: UInt8 """The red channel, in [0, 255].""" # Field docstring comptime MAX: UInt8 = 255 """The maximum value for any channel.""" # `comptime` docstring def to_hex(self) -> String: """Converts the color to a hex string.""" # Method docstring ... ``` Place module docstrings as the first string in the file. They describe the module's purpose, summarize its contents, and help documentation tools generate module-level reference pages. Place package docstrings in `__init__.mojo` files. They describe the package's purpose, summarize its exported modules, and provide package-level documentation. ## Summary line A docstring's first sentence is its summary. Summaries appear in index views and search results. | Declaration | Pattern | Examples | |---------------------|-----------------------------------|-------------------------------------------------------| | Function or method | Present-tense verb | `Clamps a value to the range [low, high].` | | | | `Converts a list of integers to a JSON array string.` | | Struct or trait | Noun phrase or present-tense verb | `A fixed-capacity circular buffer.` | | | | `Supports hashing to a fixed-size integer digest.` | | Field or `comptime` | Noun phrase | `The red channel, in [0, 255].` | Separate the summary from additional body text with a blank line. Start the summary with a capital letter and avoid repeating the declaration name. Prefer ending the summary with a period; the compiler also accepts `!`, `?`, or a closing backtick (see [Compiler checks](#compiler-checks)). ## Labeled content Structured sections include a labeled header followed by indented `name: Description.` entries. The doc generator automatically includes type information, so you don't need to repeat it in the description. The compiler validates some common structured sections against the declaration and warns about mismatches and missing entries: | Label | Documents | |----------------|-------------------------| | `Parameters:` | Compile-time parameters | | `Args:` | Runtime arguments | | `Returns:` | Return values | | `Raises:` | Error conditions | For example: ```mojo def resize[dtype: DType]( data: List[Scalar[dtype]], size: Int, fill: Scalar[dtype] = 0, ) raises -> List[Scalar[dtype]]: """Resizes a list by truncating it or padding it with a fill value. Parameters: dtype: The element type of the list. Args: data: The source list to resize. size: The target length. fill: The value used to pad the list when growing it. Returns: A new list with length `size`. Raises: An error if `size` is less than or equal to zero. """ ... ``` Mojo doesn't define a canonical set of section labels. Any `Label:` starts a new section. ### `Parameters` and `Constraints` Use `Parameters:` to document compile-time parameters when their role, behavior, or requirements are not obvious from the declaration. Use inline `Constraints:` clauses for simple parameter requirements: ```mojo Parameters: size: The static capacity. Constraints: Must be a power of two. dtype: The element type. Constraints: Must be a floating-point type. ``` Use a standalone `Constraints:` section for requirements that span multiple parameters, depend on the target architecture, or are not self-evident to most users: ```mojo def dot[size: Int]( a: SIMD[DType.float32, size], b: SIMD[DType.float32, size], ) -> Float32: """Computes the dot product of two SIMD vectors. Constraints: - `size` must be a power of two. - The target must support AVX2 or NEON. """ ... ``` ### `Args` Document each argument with its name and role. Mojo uses `Args` instead of `Arguments`. Indent continuation lines relative to the argument name: ```mojo Args: stride: The step between sampled indices. A stride of 1 returns all elements; a stride of 2 returns every other element. ``` ### `Examples` `Examples:` is not compiler-checked, but it is widely used in Mojo documentation. Use `Examples:` to show how to use an API in practice. It's normally the last section in a docstring. Example code is usually left-aligned with the label. Use fenced code blocks with the `mojo` language tag for syntax highlighting. ````text Examples for a hypothetical `find()` API: ```mojo var names: List[String] = ["alice", "bob", "carol"] print(names.find("bob").value()) # 1 print(names.find("dave") is None) # True var numbers: List[Int] = [1, 2, 3, 4, 3, 9, 3] while idx := numbers.find(3): _ = numbers.pop(idx.value()) print(numbers) # [1, 2, 4, 9] ``` ```` ### Custom labels Mojo supports custom section labels. The following are recommended conventions: | Label | Documents | |------------------|----------------------------------------------| | `Preconditions:` | Runtime conditions the caller must satisfy | | `Performance:` | Performance characteristics and tradeoffs | | `Safety:` | Safety requirements and undefined behavior | | `See:` | Related APIs, concepts, and references | Use `Preconditions:` for runtime conditions the caller must satisfy before calling, where a violation aborts the program (for example, a runtime assertion) rather than raising a catchable error. Choose among `Preconditions:`, `Constraints:`, and `Raises:` by how the condition is enforced: - `Preconditions:` for a runtime condition on the caller that aborts execution when violated and can't be caught. - `Constraints:` for a compile-time requirement that fails compilation when violated. - `Raises:` for a runtime condition that raises a catchable error. Use `Performance:` for complexity and for runtime behavior that is not obvious from complexity alone, such as allocation behavior, vectorization, scheduling, latency, I/O costs, or architecture-specific performance characteristics. Use `Safety:` for requirements, invariants, and operations that can lead to undefined behavior, memory safety issues, invalid references, or other unsafe states when used incorrectly. Use `See:` to link to related APIs, standards documents, algorithms, and external references. Mojo also accepts other labels, such as `Notes:`, but prefer putting that information in the docstring body rather than in a separate `Notes:` section. ### Section order Mojo doesn't enforce an order among sections, but a consistent order helps readers scan. Recommended order: `Parameters:` → `Args:` → `Returns:` → `Raises:` → `Preconditions:` → `Constraints:` → `Safety:` → `Performance:` → `See:` → `Examples:` Include only the sections that apply, and put `Examples:` last. ## Hidden elements ### `@doc_hidden` The `@doc_hidden` decorator excludes a declaration from generated documentation. The declaration still compiles normally but produces no documentation output. ```mojo @doc_hidden def _internal_helper(data: Pointer[UInt8, MutAnyOrigin]) -> Int: pass ``` Common uses include: - Hiding lifecycle and dunder methods not intended for direct use - Hiding implementation details such as private methods and helpers - Hiding deprecated internals kept for backward compatibility. See also: `@deprecated` ### Hidden example lines Prefix a line in a docstring example with `%#` to hide it from generated documentation. The line remains visible in the source file. `mojo doc` removes `%#` lines from generated output: ```mojo """ ... %# var result = format_result(0.857) print(result) # 85.7% ... """ ``` Common uses include: - Hiding setup code such as imports, helper functions, and temporary variables - Showing expected output in source examples without rendering it in generated documentation ## Inline formatting Docstrings support Markdown, inline code formatting, escape sequences, KaTeX syntax, and HTML tags. For example: | Content | Syntax | |-------------------------------------------------|---------------------------------------------------| | API names (types, functions, fields, arguments) | `` `Int` ``, `` `append()` ``, `` `pop(index)` `` | | Literal backslash in a code block | `\\\\` (renders as `\\`) | | Inline math | `$$x^y$$` | | Block math | `$$` on its own line, formula, `$$` | | Literal `$$` in text | `$$` | String escape sequences are honored everywhere, including inside code blocks. For example, `\n` produces a newline, while `\\t` produces the two-character sequence `\t` in a code example. KaTeX syntax supports mathematical notation in docstrings, including algorithms, formulas, and complexity annotations: - Double KaTeX backslashes: `\\frac`, `\\|`, `\\cdot` - Block formulas render centered; inline formulas render in text flow - `$$` is ignored inside backticks and fenced code blocks ## Compiler checks Mojo validates docstrings during compilation and reports common issues. For example: ```sh $ mojo optionalref.mojo optionalref.mojo:5:8: warning: doc string summary should begin with a capital letter or non-alpha character, but this begins with 'a' """an error type for when an empty `OptionalRef` is accessed""" ^ optionalref.mojo:5:8: warning: doc string summary should end with a period '.', exclamation mark '!', question mark '?', or backtick '`', but this ends with 'd' """an error type for when an empty `OptionalRef` is accessed""" ^ ``` `mojo doc` performs additional integrity checks. Use `--diagnose-missing-doc-strings` to report missing docstrings: ```sh $ mojo doc --diagnose-missing-doc-strings optionalref.mojo optionalref.mojo:1:1: warning: public module 'OptionalRef' is missing a doc string @fieldwise_init ^ optionalref.mojo:2:8: warning: struct takes parameters, but has no 'Parameters' in doc string struct EmptyOptionalRefError[T: Movable]( ^ ``` ### Validation modes Mojo uses two validation modes: - _Strict_ for public APIs - _Normal_ for private and internal declarations A declaration is public when it: - Doesn't start with `_` - Is not marked `@doc_hidden` - Is not synthesized - Is at module scope or a member of a public `struct` or `trait` These modes apply to both `mojo` and `mojo doc`. ### Missing docstrings ```sh mojo doc --diagnose-missing-doc-strings -Werror -o /dev/null stdlib/std/ ``` Reports public declarations without docstrings. `-Werror` converts warnings into errors. ### Universal checks These checks apply during compilation: ```sh mojo /path/to/file.mojo ``` **Section structure:** - Overindented section label - Duplicate section - Empty section **`Args:` and `Parameters:` entries:** - Entry names a missing argument or parameter - Duplicate entry - Entry out of declaration order - Missing entry description - Missing documented argument or parameter **`Returns:` and `Raises:` consistency:** - `Returns:` on a function without a return value - `Raises:` on a function that is not `raises` ### Strict mode checks Strict mode applies to public declarations. **Summary sentences, descriptions, and section body text:** - Must begin with a capital letter or non-alpha character - Must end with `.`, `!`, `?`, or `` ` `` This includes text in sections such as `Constraints:`, `Returns:`, and `Raises:`. ### Strict mode with `--diagnose-missing-doc-strings` This mode enables the strictest validation, such as in CI. Mojo will flag the following issues. **Missing docstrings:** - Public functions and methods - Public structs and traits - Public fields and `comptime` declarations - Public modules **Missing required sections on functions:** - `Args:` for functions with arguments - `Parameters:` for declarations with required parameters - `Returns:` for functions with return values - `Raises:` for `raises` functions **Missing required sections on non-functions:** - `Parameters:` for declarations with required parameters **Not checked:** - `Constraints:` - Custom labels such as `Notes:`, `Performance:`, and `Safety:` --- ## Mojo expression reference {/* VERIFIED: ParserExprs.cpp, ExprNode.h, ParserBase.h, ExprNodes.h */} An *expression* is any piece of code that produces a value. Expressions are the building blocks of computation: you combine them with operators, pass them as arguments, assign their results to variables, and use them as conditions in control flow. ## Identifier expressions An identifier refers to a named element: a variable, function, type, or module. Using an identifier in an expression gives you the thing it refers to: ```mojo score # a variable Int # a type range # a function ``` ## Parenthesized expressions Parentheses group subexpressions, overriding default precedence: ```mojo (a + b) * c # add a and b, multiply by c (x) # just x ``` Parentheses also let expressions span multiple lines without using backslash escapes: ```mojo var result = ( first_value + second_value + third_value ) ``` ## Tuples A *tuple* is a fixed-size, ordered group of values. Commas create tuples, not parentheses: ```mojo var a = 2, 3 # tuple without parentheses var b = (2, 3) # same tuple with parentheses var x, y = b # x is 2, y is 3 ``` Use a trailing comma to create a one-element tuple. Without it, `(1)` is just the integer `1` in parentheses: ```mojo () # empty tuple (1,) # one-element tuple (1, 2, 3) # three-element tuple ``` Tuples support indexing: ```mojo var point = (10, 20) print(point[0]) # 10 ``` ## Collection displays The compiler calls these *displays*. Displays are similar to literals, but unlike literals, displays can contain expressions as well as fixed values. Literals cannot contain expressions. For example: ```mojo [1, 2, 3] # list literal [1, 1+1, 1+1+1] # list display ``` ### Lists A *list display* creates a list from comma-separated values: ```mojo var empty: List[Float32] = [] var numbers = [1, 2, 3] var strings = ["one", "two", "three",] ``` Mojo allows trailing commas after all collection elements, including the final one. ### Dictionaries A *dict display* maps keys to values with `:` between each pair: ```mojo var empty: Dict[String, Int] = {} var ages = {"Alice": 30, "Bob": 25} ``` ### Sets A *set display* uses braces with values but no colons: ```mojo var primes = {2, 3, 5, 7} ``` Don't mix set and dict syntax. `{1, 2}` is a set. `{1: 2}` is a dict. ```mojo {"a": 1, 2} # Error: expected 'key: value' in dictionary expression {1, "b": 2} # Error: cannot have a 'key: value' pair in set initializer ``` **Sets are not initializer lists**. Brace syntax also serves as an *initializer list* that creates an instance of an inferred type. Without type context, the compiler can't distinguish a set from an initializer list, so the distinction is resolved at type-check time. Initializer lists can include positional values and keyword arguments: ```mojo {x, y} # set or initializer list, without context {z=4, "foo"} # initializer list with keyword argument ``` Initializer lists are syntactic sugar for initializer calls. `{1, "hello"}` is equivalent to `T(1, "hello")` when the type `T` is known from context. Use them for passing initialized instances as arguments: ```mojo process({1, "hello"}) # type inferred from signature var x: T = {} # type inferred from variable declaration, calls T() ``` **Sharp edge: set displays are core Mojo syntax but the `Set` type is not**. You must import `Set` from the standard library to use it as a type: ```mojo from std.collections import Set # Required to use Set type from std.testing import assert_equal def main() raises: var display_set = {1, 2, 3} # A set with elements 1, 2, and 3 assert_equal(len(display_set), 3) # The length of the set is 3 var empty_set = Set[Int]() # An empty set empty_set.add(4) # Add an element to the set empty_set.add(4) assert_equal(len(empty_set), 1) # Sets do not allow duplicate elements ``` ## Member access The dot operator accesses an attribute or method on a value: ```mojo var length = text.count() var x = point.x var name = person.name.upper() ``` Chaining is left to right: `a.b.c` accesses `c` on the result of `a.b`. ## Calls A *call expression* invokes a function or constructs a value by appending `()` to an expression: ```mojo print("hello") var result = compute(a, b) var p = Point(1.0, 2.0) ``` ### Positional and keyword arguments Arguments before any keyword argument are positional. Keyword arguments use `name=value` syntax. ```mojo def greet(name: String, loud: Bool = False): print(t"Hello, {name if not loud else name.upper()}!") greet("Alice") # Hello, Alice! greet("Alice", loud=True) # Hello, ALICE! greet(name="Bob") # Hello, Bob! ``` Positional arguments can't follow keyword arguments: ```mojo # greet(loud=True, "Alice") # Error: positional argument follows keyword argument ``` Keyword arguments can't be repeated: ```mojo greet(name="Alice", name="Bob") # Error: duplicate keyword argument 'name' ``` ## Subscripts and slices Square brackets after an expression look up a value by index(es) or key(s): ```mojo var item = collection[0] var value = mapping["key"] var cell = matrix[i, j] ``` ### Slices Colons inside square brackets create *slices*. Slices select a range of elements using `start:stop` or `start:stop:stride`: ```mojo var items = [0, 1, 2, 3, 4, 5] var first_three = items[0:3] # [0, 1, 2] (3 not included) var from_three = items[3:] # [3, 4, 5] var every_other = items[::2] # [0, 2, 4] var reversed = items[::-1] # [5, 4, 3, 2, 1, 0] ``` All three parts are optional. Start defaults to the beginning, stop defaults to the end, and stride defaults to 1. The element at the stop position isn't included in the result. ## Ternary conditional The `if`-`else` expression selects between two values based on a condition: ```mojo var label = "even" if x % 2 == 0 else "odd" ``` The condition follows `if`, and the alternate value follows `else`. If the condition is true, the expression evaluates to the first value. If false, the alternate. Ternary expressions are right-associative and can be chained: ```mojo var size = ( "small" if n < 10 else "large" if n > 100 else "medium" ) ``` This groups as `"small" if n < 10 else ("large" if n > 100 else "medium")`. ## Walrus operator Regular assignments (`=`) are statements, not expressions. They don't produce a value. The walrus operator (:=) is the expression form of assignment. It binds a value to a name and evaluates to that value. You can assign and use the result in a single step: ```mojo def main() raises: var items = List(range(20)) var n: Int if (n := len(items)) > 10: print(n) var name: String # declare before use while name := input("Prompt: "): # input is raising print("Hello,", name) ``` Strings are truthy. The loop ends when you press Return without entering text. The name must already be declared. Walrus only assigns into existing values. Walrus assignment is most useful for temporary values created as part of an expression. Its binding remains available for the rest of its scope. Walrus assignment doesn't imply ownership, reference, or memory semantics. Plus, the value on the right needn't exist in memory; it might exist only in a register. The walrus operator has the lowest precedence of any expression operator. Use parentheses when needed to make your intent clear: ```mojo if item := list[idx] < 50: # binds comparison result print(t"{item} is under 50") # "True is under 50" if (item := list[idx]) < 50: # binds list item print(t"{item} is under 50") # "(actual number) is under 50" ``` ## Compile-time expressions `comptime` forces an expression to evaluate at compile time. Parentheses are required: ```mojo def heavy_calculation() -> Int: var sum = 0 for i in range(1_000_000): sum += i return sum # var x = comptime heavy_calculation() # Error: requires parentheses var x = comptime(heavy_calculation()) # O(1) at runtime print(x) # 499999500000 ``` The loop runs once during compilation. At runtime, `x` is a constant. If the expression can't be evaluated at compile time, the compiler reports an error. Mojo also provides built-in expressions for compile-time type introspection. These look like function calls but they're keywords that operate on types and traits at compile time: ```mojo type_of(x) # type of an expression conforms_to(T, Trait) # test trait conformance origin_of(x) # origin of a reference ``` These three expressions are used for reflection, conditional type conformance, and origin sets. They return compiler-internal types and won't print. ## Function type expressions A function type expression describes the signature of a function as a type: ```mojo def() -> Int def(Int, Int) -> Int def(var value: String) -> None def() raises -> String def(T) -> T ``` Function type expressions can include argument types with conventions, return types, and effects like `raises`. ## Lambda expressions A `lambda` is an anonymous, single-expression function. Its arguments are parenthesized and typed, like a function declaration, and its body is a single expression with no `return`: ```text lambda [[parameter-list]] [(argument-list)] [effects] [{capture-list}] [-> ResultType] : expression ``` For example, this lambda returns its argument value incremented by 1: ```mojo var inc = lambda (x: Int) {} -> Int: x + 1 var y = inc(4) # 5 ``` For complete syntax, semantics, and examples, see the [lambda expressions reference](/docs/reference/lambda-expressions). ## Comprehension expressions A comprehension is a concise way to build a new collection by iterating over existing values and optionally filtering or transforming them. It replaces common loop-and-append patterns with a single expression. ### List comprehensions List comprehensions create lists: ```mojo var squares = [x * x for x in [0, 1, 2, 3, 4] if x % 2 == 0] # [0, 4, 16] var positive = [x for x in range(-3, 3) if x > 0] # [1, 2] ``` **Syntax:** `[expr for pattern in iterable if condition]` - Multiple `for` clauses create nested iteration - `if` clauses filter elements ### Set comprehensions Set comprehensions create sets. Sets don't store duplicates, so you may get fewer elements than iterations. For a Fibonacci generator that starts with `fib(0)=1` and `fib(1)=1`, the first 6 Fibonacci numbers are `1, 1, 2, 3, 5, 8`: ```mojo var fibs = {fib(x) for x in range(6)} # {1, 2, 3, 5, 8}, 5 elements from 6 iterations ``` **Syntax:** `{expr for pattern in iterable if condition}` ### Dictionary comprehensions Dictionary comprehensions create dictionaries: ```mojo var dict_squares = {x: x * x for x in range(3)} # {0: 0, 1: 1, 2: 4} var lengths: Dict[String, Int] = { k: len(k) for k in ["one", "two", "three", "four"] } # {one: 3, two: 3, three: 5, four: 4} ``` **Syntax:** `{key_expr: value_expr for pattern in iterable if condition}` ### Comprehension clauses Comprehensions support multiple `for` and `if` clauses: ```mojo var products = [ (x, y, x * y) for x in range(3) for y in range(3) if (x + y) % 2 == 0 ] # [(0, 0, 0), (0, 2, 0), (1, 1, 1), (2, 0, 0), (2, 2, 4)] ``` Clauses are evaluated left to right: - Each `for` introduces a new iteration variable - Each `if` filters based on the current values --- ## Mojo function declarations reference {/* VERIFIED: TokenKinds.def, Signatures.h, Signatures.cpp, ParserStmts.cpp, ParserBase.h, ParserBase.cpp, ASTDecl.h, ASTDecl.cpp, ExprNode.h, OverloadFitness.h, OverloadFitness.cpp, OverloadSet.h, OverloadSet.cpp, ParamInf.h, ParamInf.cpp, DeclResolution.cpp (return-type-only redefinition diagnostic). Consulted: ExprNodes.h, ExprNodes.cpp, ParserExprs.cpp, ASTType.h, MojoDiags.h */} {/* NOT VERIFIED: "Copy initializers can't raise" — likely enforced via trait/IR; no specific error string surfaced. "out arguments can't have defaults" — doc claim at line 365; no specific compiler error found, but plausible. "Copy ctor copy argument must use the default convention" — specific convention claim; didn't trace. "Import + local def with the same name" — verified by experiment to produce `invalid redefinition of '': cannot overload with this non-function definition`, not silent shadowing as the existing manual claims. Reference text was corrected accordingly. The aliased-import workaround was also confirmed to compile. */} A *function declaration* introduces a named, callable unit of code. Every function in Mojo starts with the `def` keyword: ```mojo def greet(name: String) -> String: return "Hello, " + name ``` The simplest function has a name, empty parentheses, and a body: ```mojo def do_nothing(): pass ``` ## Function names Function names must be valid identifiers. Backtick-escaped identifiers allow keywords as function names: ```mojo def `import`(): print("In `import`") def main(): `import`() # In `import` ``` ## Function signatures {/* VERIFIED: From ParserStmts.cpp, Signatures.h */} ```text def name(argument-list) -> ReturnType: body def name[parameter-list](argument-list) -> ReturnType: body def name(argument-list) raises -> ReturnType: body def name[parameter-list](argument-list) -> ReturnType where constraint: body def name[parameter-list](argument-list) raises -> ReturnType where constraint: body ``` A signature can include a name, a parameter list, an argument list, effects, a return type, and a `where` clause. Only the parentheses and the colon are required. *Arguments* are runtime values in parentheses. *Parameters* are compile-time values in square brackets. In other languages these are both called "parameters". Mojo distinguishes them to avoid confusion: ```mojo # T must be both `Comparable` (to test with `<` and `>`) and # `ImplicitlyCopyable` or you won't be able to return def clamp[T: Comparable & ImplicitlyCopyable]( val: T, lo: T, hi: T, ) -> T: if val < lo: return lo if val > hi: return hi return val ``` ## Markers Three markers divide parameter and argument lists into zones that control how callers pass values: | Marker | Arguments | Parameters | |--------|-----------------|-----------------| | `//` | No | Infer-only | | `/` | Positional-only | Positional-only | | `*` | Keyword-only | Keyword-only | Markers must appear in this order: `//`, then `/`, then `*`. Each can appear once. `/` can't be first in the list, and `*` can't be last. :::note Mojo markers follow Python's convention from PEP 570 and PEP 3102. ::: ### Infer-only marker (`//`, parameters only) `//` separates infer-only parameters from named parameters. The compiler deduces infer-only parameters from call-site arguments: ```mojo def inferred_type[T: Writable, //](value: T): print(t"Value is {value}. Type is {reflect[T].name()}.") def main(): inferred_type(5) # Value is 5. Type is SIMD[DType.int, 1]. inferred_type("Hello") # Value is Hello. Type is String. ``` Infer-only parameters can't be specified positionally: ```mojo # Error because 'inferred_type' got 1 positional parameter # but expected none. inferred_type[Int](5) ``` Keyword syntax bypasses this restriction: ```mojo inferred_type[T=Int](5) # OK: Value is 5. Type is Int. # Error because value passed to 'value' cannot be converted from # 'StringLiteral["Hello"]' to 'Int' inferred_type[T=Int]("Hello") ``` Inference isn't limited to infer-only parameters. With enough context, the compiler can infer named parameters too: ```mojo def add[T: Intable](a: T, b: T) -> Int: return Int(a) + Int(b) def main(): print(t"Sum is {add[Int](1, 2)}.") # Explicit T print(t"Sum is {add(1, 2)}.") # Inferred T print(t"Sum is {add[Float64](4.5, 1.2)}.") # Explicit print(t"Sum is {add(4.5, 1.2)}.") # Inferred ``` ### Positional-only marker (`/`) Everything before `/` is positional-only. Callers must pass these values by position, not by name: ```mojo def div(a: Int, b: Int, /): return a // b div(10, 3) # OK div(a=10, b=3) # Error ``` ### Keyword-only marker (`*`) Everything after `*` is keyword-only. Callers must pass values by name, whether parameters or arguments: ```mojo def configure(*, verbose: Bool, retries: Int): # ... configure(verbose=True, retries=3) # OK configure(True, 3) # Error ``` A `*args` variadic argument has the same effect on arguments that follow it: ```mojo def sum(*values: Int, name: String) -> Int: print(name, end=": ") var total = 0 for value in values: total += value return total def main(): print(sum(1, 2, 3, name="total")) # total: 6 # print(sum(1, 2, 3, "subtotal")) # Error because missing required keyword argument ``` ## Default values Arguments can have default values. Once a default appears, every following positional argument must also have one: ```mojo def connect( host: String = "www.modular.com", port: Int = 80, ): print(t"Connecting to {host}:{port}") def main(): connect() # Connecting to www.modular.com:80 connect(port=8080) # Connecting to www.modular.com:8080 ``` ```mojo def my_function(x: Int, y: Int = 0, z: Int = 0) -> Int: return x + y + z # Error because required positional argument follows optional # positional argument # def wrong(x: Int, y: Int = 0, z: Int): # return x + y + z ``` Keyword-only arguments are exempt from the ordering rule. They can mix required and optional freely: ```mojo def configure(*, retries: Int = 3, verbose: Bool): pass ``` Parameters also support defaults. ## Function constraints A `where` clause constrains compile-time parameters. It appears at the end of the declaration, after the return type (or after the argument list if there's no return type): ```mojo comptime LESS_THAN: Int32 = -1 comptime EQUAL: Int32 = 0 comptime GREATER_THAN: Int32 = 1 def compare[T: AnyType]( x: T, y: T, ) -> Int32 where conforms_to(T, Comparable): if x < y: return LESS_THAN elif x > y: return GREATER_THAN else: return EQUAL def main(): print(compare(5, 10)) # -1 (LESS_THAN) print(compare(7, 7)) # 0 (EQUAL) print(compare("Z", "A")) # 1 (GREATER_THAN) ``` `where` clauses can express complex constraints, such as limiting SIMD vector sizes to certain powers of 2: ```mojo def process[ n: Int, ](data: SIMD[.float32, n]) -> Float32 where ( n == 1 or n == 2 or n == 4 or n == 8 or n == 16 or n == 32 ): var sum: Float32 = 0.0 for i in range(n): sum += data[i] return sum def main(): var data = SIMD[.float32, 16](255.0) var sum = process[n=16](data) print(t"Sum: {sum}") # Sum: 4080.0 ``` `where` clauses belong at the end of a declaration: ```mojo # Correct: the `where` clause follows the signature. def correct[n: Int]() where n > 0: pass ``` A `where` clause inside a parameter list is invalid. Add it to the end of the declaration: ```mojo # Wrong: `where` is not allowed inside a parameter list. def wrong[n: Int where n > 0](): pass ``` A `where` clause in an argument list is invalid: ```mojo # Wrong: `where` clauses can only be used with compile-time parameters. def wrong(x: Int where x > 0): pass ``` A [`thin`](#thin) function *type* can carry its own trailing `where` clauses, constraining the parameters that type declares. See [Constrained function types](#constrained-function-types). ## Argument conventions An *argument convention* controls how an argument value passes to a function. It appears before the argument name. ### `mut` The caller's value is passed by mutable reference. Changes inside the function are visible to the caller: ```mojo def double_it(mut x: Int): x *= 2 ``` `mut` arguments can't have default values: ```mojo # Error because 'mut' arguments may not have defaults def wrong(mut x: Int = 0): pass ``` ### `var` The function receives an owned copy. If the caller transfers ownership with `^`, the original becomes inaccessible. Otherwise the value is copied and the caller keeps access: ```mojo def consume(var s: String): s += "!" print(s) def main(): var greeting = "Hello" consume(greeting) # Hello! (copied) print(greeting) # Hello consume(greeting^) # Hello! (moved) # print(greeting) # Error because uninitialized after move ``` ### `out` An `out` argument is the function's return slot. Only one `out` argument is allowed. It replaces the `->` return type: ```mojo def make_int(out result: Int): result = 42 def main(): var x = make_int() print(x) # 42 ``` A function can't use both `out` and `-> Type`: ```mojo # Error because function cannot have both an 'out' argument # and an explicit result type def wrong(out result: Int) -> Int: result = 0 ``` ### `deinit` The function takes ownership and destroys the value. Required for `self` in `__deinit__()` and the argument in move initializers: ```mojo struct Resource: var handle: Int def __deinit__(deinit self): _release(self.handle) ``` ### `ref` Passes a reference with an explicit *origin* specifier. The origin tracks where the reference came from: ```mojo def get_first[T: Copyable](ref data: List[T]) -> ref[data[0]] T: return data[0] def main(): var data: List[String] = ["one", "two", "three"] ref first = get_first(data) # mutable because `data` is mutable print(first) # one first = "Первый" print(data) # ['Первый', 'two', 'three'] ``` ### Default convention Without a convention, the argument is an immutable read-only reference. The caller keeps ownership: ```mojo def length[T: Copyable](s: List[T]) -> Int: return len(s) ``` ## Variadic arguments Variadic arguments accept a varying number of values ("indefinite arity"). Functions like print use variadic arguments to accept any number of values. ### Homogeneous variadics `*` before the argument name accepts any number of positional arguments of the same type (homogeneous arguments): ```mojo def sum_all(*values: Int) -> Int: var total = 0 for v in values: total += v return total ``` ### Variadic packs `*` before both the name *and* the type annotation creates a *variadic pack* that accepts arguments of different types (heterogeneous arguments): ```mojo def print_all[*Ts: Writable](*args: *Ts): comptime for idx in range(args.__len__()): print(args[idx], end=" ") print() def main(): print_all("Hello", 42, 3.14) # Hello 42 3.14 ``` :::note Why not `len(args)`? `args` has a `VariadicPack` type, and `VariadicPack.__len__()` is a `@staticmethod`, so `args.__len__()` is the same as `type_of(args).__len__()`. The compiler can evaluate it at compile time. `len(args)` doesn't work because `args` is a dynamic value, so `len(args)` is a dynamic expression. You can't use a dynamic value to drive a `comptime for`. ::: ### Variadic restrictions A function can have at most one `*args`. Variadic arguments can't have default values: ```mojo # Error because variadic arguments may not have defaults def wrong(*args: Int = 0): pass ``` `out` arguments can't be variadic: ```mojo def wrong(out *results: Int): pass ``` ## Function effects Effects appear after the closing parenthesis and before `->`. ### `raises` {#raises} Declares that the function can raise an error. An optional error type can follow `raises`: ```mojo def parse(text: String) raises -> Int: # ... def parse_strict(text: String) raises SomeError -> Int: # ... ``` A function can specify at most one error type after `raises`. ### `thin` {#thin} Used only in function *types*, `thin` indicates a function pointer type (not a closure) and ensures the function value doesn't capture values from its defining scope. Don't use `thin` in function declarations. ```mojo def map[ T: Copyable, U: Copyable ](f: def(T) thin -> U, input: List[T]) -> List[U]: # Used as type var result: List[U] = [] for item in input: result.append(f(item)) return result^ # `square` doesn't capture values. It can be passed to a thin type def square(x: Int) -> Int: return x * x def main(): var nums: List[Int] = [1, 2, 3] var squares = map(square, nums) print(squares) # Output: [1, 4, 9] ``` #### Constrained function types {#constrained-function-types} A `thin` function type that declares parameters can constrain them with trailing `where` clauses, written after the result type. The constraint is part of the type: callers must prove it holds for the parameters they bind. ```mojo comptime Kernel = def[w: Int](Int) thin -> None where ( w > 0, "width must be positive" ) def apply[F: Kernel](x: Int): F[4](x) # ok F[0](x) # error: violated constraint ``` Constraints are contravariant. A function satisfies the type when its own `where` clause is implied by the type's, or when it has none — the type promises its callers more than the function demands. A function that demands more than the type promises is rejected. Only `thin` function types accept a `where` clause. The clause binds to the innermost function type, so a declaration-level `where` that follows a function-type result needs that result parenthesized: ```mojo # The `where` clause constrains the returned function type. def inner_constraint[n: Int]() -> def() thin -> None where n > 0: ... # The `where` clause constrains `outer_constraint` itself. def outer_constraint[n: Int]() -> (def() thin -> None) where n > 0: ... ``` ### `abi("C")` {#abi-c} Declares that a function uses the C calling convention. Because C has no closure mechanism, `abi("C")` normally appears together with `thin` in function types: ```mojo # `add` is compiled with the C calling convention, so it can be called # from C or stored in a C-ABI function pointer. def add(a: Int32, b: Int32) abi("C") -> Int32: return a + b def main(): var fp: def(Int32, Int32) thin abi("C") -> Int32 = add print(fp(1, 2)) # 3 ``` :::caution Don't combine non-Mojo `abi()` effects with raising functions. Raising functions change calling conventions in non-obvious ways. The Mojo compiler: - accepts `def (String) abi("Mojo") raises` - rejects `def (String) abi("C") raises` `abi("Mojo")` is already the default. You don't need to specify it. ::: ## Return type `->` introduces the return type. It appears after any effects: ```mojo def square(x: Int) -> Int: return x * x ``` Without `->`, the function returns `None`. ## Special methods Certain method names have enforced signatures. The compiler checks argument count, conventions, and return types. ### Initializers An initializer must have an `out self` result: ```mojo struct Point: var x: Int var y: Int def __init__(out self, x: Int, y: Int): self.x = x self.y = y ``` Without `out self`, the compiler rejects the method: ```mojo # Error because __init__ method must return Self type # with 'out' argument def __init__(self): pass ``` ### Copy initializers A copy initializer uses a single keyword-only argument named `copy`: ```mojo def __init__(out self, *, copy: Self): self.x = copy.x self.y = copy.y ``` Copy a value with `Type.__init__(copy=value)`, `Type(copy=value)`, or `value.copy()`: - The `copy` argument must use the default convention of a readable immutable reference. - Copy initializers can't raise. - Trivial types can't define a copy initializer. - Conforming to `Copyable` or `ImplicitlyCopyable` automatically generates a copy initializer if one isn't already defined. - If the type isn't compatible with copying, the compiler rejects the conformance. Don't confuse copying with casting. `Type(value)` casts in Mojo. `Type(copy=value)` and `value.copy()` copy. Prefer the explicit `.copy()` call over the `copy` keyword argument form for idiomatic Mojo style. ### Move initializers A move initializer uses a single keyword-only argument named `move`: ```mojo def __init__(out self, *, deinit move: Self): self.x = move.x self.y = move.y ``` Call `Type.__init__(move=value)` or `Type(move=value)`: - The `move` argument must use the `deinit` convention. - Move initializers can't raise. - `RegisterPassable` types can't define a move initializer. They're always movable by copying a register. ### Deinitializers A deinitializer takes `deinit self`: ```mojo def __deinit__(deinit self): _release(self.handle) ``` - Deinitializers can't raise. - Trivial types can't define a deinitializer. You can call `value.__deinit__()` explicitly but the compiler calls it automatically at the value's last use, via ASAP destruction. Prefer custom deinitializers for [explicit destruction](/docs/manual/lifecycle/death/#explicitly-destroyed-types). ## Nested functions Functions can be defined inside other functions. Nested functions can capture values from the enclosing scope as *closures*: ```mojo def outer(x: Int) -> Int: def inner() {imm} -> Int: # Read-only references (`imm`) to outer scope return x + 1 return inner() ``` The compiler resolves nested function bodies immediately so captures bind correctly. ## Static methods `@staticmethod` makes a struct method callable without an instance. Static methods don't take `self`: ```mojo struct MathUtils: comptime pi: Float64 = 3.141592653589793 @staticmethod def square(x: Int) -> Int: return x * x def main(): print(MathUtils.square(5)) # 25 print(MathUtils.pi) # 3.141592653589793 ``` ## Function overloads {#function-overloads} {/* VERIFIED: OverloadFitness::isBetter in OverloadFitness.cpp, filterForBestCandidates in OverloadSet.cpp, ParamInf.cpp. "Cannot overload on return type only" diagnostic: DeclResolution.cpp around the `redefinition of function` emit. Raises-only redefinition behavior: verified by experiment to emit `redefinition of function '' with identical signature` from the same DeclResolution.cpp path; `raises` is not part of the signature for overload-set purposes. */} A *function overload* is one of two or more function declarations that share a name but differ in their signature. The compiler picks one of them at each call site. This is *static dispatch*: there's no runtime lookup. The choice is fixed when the call is type-checked. An *overload set* is the collection of overloads the compiler considers at a call site. It contains the declarations that share the same name in the same scope. Use overloads to give one operation more than one shape: different argument types, different argument counts, different keyword names, different `self` conventions, or different compile-time parameter signatures. ```mojo def add(x: Int, y: Int) -> Int: return x + y def add(x: String, y: String) -> String: return x + y def main(): print(add(1, 2)) # 3 print(add("Hi, ", "Mojo")) # Hi, Mojo ``` ### Where overload sets form Each scope builds its own overload set: - *Module scope.* Declarations with the same name in the same module form one overload set. - *Struct scope.* Methods on a struct (including `@staticmethod`) form one overload set per method name. - *Trait scope.* Required and provided methods on a trait form one overload set per method name. An overload set can't be extended across scopes. An import brings the name in as a non-function reference: you can't add another overload to it from your own module, and you can't redefine it. A local declaration that collides with an import produces an error. To avoid the error, use an alias: ```mojo from some_package import add as imported_add def add(x: Float64, y: Float64) -> Float64: return x + y # `add` resolves to the local definition. # `imported_add` resolves to the imported one. ``` ### What the compiler considers Overload resolution 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 `@staticmethod`. - Whether a deinitializer is `@implicit`. Overload resolution doesn't look at the return type or any other context surrounding the call. ### Resolution rules The compiler discards every candidate whose signature can't be satisfied by the call. It then compares the remaining candidates pairwise. It applies the following rules in order until one wins. The compiler selects that candidate. 1. Pick the candidate that uses fewer implicit conversions between arguments and parameters. An empty match against an `*args` argument counts as an implicit conversion, so an exact match beats it. 2. Pick the candidate that doesn't bind non-empty variadic arguments. A signature without `*args` beats one whose `*args` argument receives at least one value. 3. Pick the candidate with fewer mismatched argument conventions. 4. Pick the candidate with a shorter parameter list. A function with no compile-time parameters beats one that declares a parameter. The parameter list also counts implicit parameters synthesized from argument types (for example, the unbound parameters of a `SIMD[...]` argument become implicit parameters on the function). After fitness comparisons, the compiler applies two tiebreakers: 5. Pick the candidate that is an instance method over a `@staticmethod` with the same name. 6. Pick the candidate that is a non-implicit deinitializer over an implicit one. If two candidates are equally good after these steps, the call is ambiguous. The compiler rejects it. :::note Rule 4 means a concrete function wins over a parameterized one. `def foo(a: Int)` beats `def foo[T: AnyType](a: T)` for a call with an `Int` argument, even though both signatures match. ::: ### Overloading parameters Functions can overload on compile-time parameters as well as on arguments: ```mojo def take_param[a: Int, b: Int](): print("take_param[a: Int, b: Int]") def take_param[a: Int, b: String](): print("take_param[a: Int, b: String]") def main(): take_param[1, 2]() # take_param[a: Int, b: Int] take_param[1, "hi"]() # take_param[a: Int, b: String] ``` ### Overloading the `self` convention A method can be overloaded by its `self` convention. The default call site uses an immutable reference for `self`, so `ref self` wins. To reach the `var self` overload, the caller transfers with `^` at the call site: ```mojo @fieldwise_init struct Counter(Copyable): var n: Int def which(ref self) -> Int: return 1 # Constrain `var self` to types where `^` actually transfers. # For `TrivialRegisterPassable` types, `^` is a no-op. This # overload would otherwise be silently unreachable. def which(var self) -> Int where not conforms_to( Self, TrivialRegisterPassable ): return 2 def main(): var c1 = Counter(0) print(c1.which()) # 1: default call uses an immutable self reference var c2 = Counter(0) print((c2^).which()) # 2: caller transfers self ``` :::caution Sharp edge For `TrivialRegisterPassable` types, the transfer operator `^` is a no-op. When using a trivial register type like `Int`, `Float64`, or similar, `(c^).which()` becomes `c.which()`. The compiler will warn you but won't reject your code. The compile-time conformance check in the `where` clause above makes a non-trivial constraint explicit. It filters the `var self` overload out for trivial register types instead of leaving it silently unreachable. ::: ### Instance methods beat static methods When both an instance method and a `@staticmethod` have the same name, a method-call expression picks the instance method (rule 5): ```mojo struct StaticOverload: def __init__(out self): pass def foo(mut self): print("instance method") @staticmethod def foo(): print("static method") def main(): var a = StaticOverload() a.foo() # instance method ``` To call the static method explicitly, use the type name: ```mojo StaticOverload.foo() # static method ``` ### Variadic candidates lose ties A signature without `*args` beats a variadic signature when both match the call, because the variadic version costs one implicit conversion for the empty pack (rule 1) or, with values supplied, loses on rule 2: ```mojo def take(x: Int): print("take(x: Int)") def take(*xs: Int): print("take(*xs: Int)") def main(): take(1) # take(x: Int) take(1, 2, 3) # take(*xs: Int): the only match. ``` ### Ambiguous calls When two candidates are equally good, the call fails: ```mojo struct MyString: @implicit def __init__(out self, s: String): pass struct YourString: @implicit def __init__(out self, s: String): pass def foo(name: MyString): print("MyString") def foo(name: YourString): print("YourString") def main(): # Error because the call is ambiguous: both overloads need exactly # one implicit conversion from `String` foo("Hello") ``` Resolve ambiguity by casting at the call site: ```mojo def main(): foo(MyString("Hello")) # MyString foo(YourString("Hello")) # YourString ``` Literals can trigger the same kind of ambiguity. An `IntLiteral` converts to both `Int` and `Float64` at equal cost, so a call like `take_param[1, 2]()` against the two overloads below is ambiguous: ```mojo def take_param[a: Int, b: Int](): pass def take_param[a: Int, b: Float64](): pass def main(): # Error because `IntLiteral` converts to both `Int` and `Float64` # at equal cost; the compiler can't pick a winner take_param[1, 2]() ``` To resolve, remove the ambiguity or remove the overload by renaming one version. ### Return types don't disambiguate Two overloads that differ only in their return type are indistinguishable to overload resolution: ```mojo def parse(s: String) -> Int: return 0 # Error because `parse` cannot overload on return type only; # the differing return type doesn't form a new overload def parse(s: String) -> Float64: return 0.0 ``` Use different argument types, an extra parameter, or a different function name instead. ### `raises` doesn't disambiguate Two functions that differ only in whether they `raises` have the same signature for overload-set purposes. The compiler rejects the second declaration: ```mojo def maybe_raise(x: Int) -> Int: return x # Error because `maybe_raise` already has this signature; # `raises` isn't part of the signature for overload purposes def maybe_raise(x: Int) raises -> Int: raise Error("nope") ``` If both behaviors are needed, give them distinct names or make the raising version take a different argument shape. ### Best practices - Use overloads when each version implements the same operation on a different shape of input. Don't overload to mean different things under the same name. - Don't rely on two implicit initializers being reachable from the same source type. Cast at the call site or make one initializer non-implicit. - Prefer overloading on argument *type* over overloading on convention. Convention-based overloads work, but they're easy to misread. - When an overload set should accept many types, write one parameterized function constrained with `where` instead of many near-duplicates. The compiler picks the concrete signature when both are present (rule 4). - Don't define a function with the same name as one you imported. The compiler rejects it. Import under an alias when you need both names. --- ## Mojo language reference The Mojo language reference provides a concise guide for Mojo's syntax, organized by grammar construct and designed for quick lookup when you need the exact rules or form of a language element. --- ## Mojo inline MLIR reference Mojo is built on [MLIR](https://mlir.llvm.org/) and exposes it directly to developers. When you need an operation that Mojo doesn't surface, such as hardware intrinsics, atomic memory orderings, or custom dialect operations, you can write the MLIR operation yourself instead of waiting for a language feature. MLIR (Multi-Level Intermediate Representation) is a compiler framework in the LLVM project. It models programs with custom, layered dialects that represent data flow, loops, and hardware-specific operations. These dialects are translated step by step into LLVM IR and then into machine code. ## Hello MLIR This example shows a minimal Mojo-MLIR program at the level of a "Hello World" implementation. It creates two MLIR index constants, adds them, and converts the result back to Mojo's `Int`: ```mojo def main(): var a: __mlir_type.index = __mlir_attr.`42 : index` var b: __mlir_type.index = __mlir_attr.`8 : index` var c = __mlir_op.`index.add`(a, b) print(Int(mlir_value=c)) # 50 ``` These built-ins work together: - `__mlir_type` sets the variable's MLIR type. - `__mlir_attr` provides a compile-time constant. - `__mlir_op` runs an MLIR operation. `Int(mlir_value=...)` converts the raw MLIR value back into Mojo. You could write `42 + 8` in plain Mojo. Inline MLIR gives you direct access to operations that Mojo doesn't expose yet, such as NVVM barriers, AMD matrix multiplies, and target-specific address spaces. ## The four built-in identifiers Mojo provides four built-in identifiers to reference MLIR from source code. Each corresponds to a common MLIR building block: | Built-in | Purpose | Produces | |---------------------|------------------------------|-------------------------| | `__mlir_type` | Reference an MLIR type | A type | | `__mlir_attr` | Reference an MLIR attribute | Compile-time value | | `__mlir_op` | Invoke an MLIR operation | Runtime value or `None` | | `__mlir_region` | Define a single-block region | Statement (no value) | You don't need to be an expert in MLIR basics to work through this page, but it helps to recognize a few core ideas: dialects, operations, attributes, types, and regions. This content focuses on how Mojo maps to those concepts. Types and attributes support two forms: dot/backtick syntax for simple names and bracket syntax for parameterized construction. ## `__mlir_type` The `__mlir_type` built-in lets you define MLIR types directly in Mojo. You can use these types in variable declarations, parameter lists, and `comptime` aliases, just as you'd use built-in types like `Float64` or `Pointer`. ### Dot and backtick syntax For simple type names that are valid identifiers, use dot syntax. Use backticks when the name includes special characters like `!`, `<`, or `>`: ```mojo var x: __mlir_type.i1 # 1-bit integer var y: __mlir_type.index # Machine-width index var z: __mlir_type.f64 # 64-bit float var a: __mlir_type.`!kgen.none` # Dialect type with ! prefix var b: __mlir_type.`!kgen.scalar` # Pop dialect scalar var c: __mlir_type.`!kgen.pointer>` # Nested pointer ``` MLIR uses short names for primitive types. Here's what you'll see most often: | MLIR name | Meaning | |-----------|---------------------------------------------| | `i1` | 1-bit integer (boolean) | | `i8` | 8-bit signless integer | | `i32` | 32-bit signless integer | | `i64` | 64-bit signless integer | | `si32` | 32-bit signed integer | | `si64` | 64-bit signed integer | | `ui32` | 32-bit unsigned integer | | `f16` | 16-bit float (IEEE half) | | `bf16` | 16-bit bfloat | | `f32` | 32-bit float | | `f64` | 64-bit float | | `index` | Machine-width integer for sizes and offsets | "Signless" means the type itself doesn't specify signed or unsigned. The operation using the value decides how to interpret it. The `s` and `u` prefixed variants (`si32`, `ui64`) carry signedness in the type. For a full list, see [MLIR's Builtin Types documentation](https://mlir.llvm.org/docs/Dialects/Builtin/#types). Dialect-defined MLIR types use the `!` prefix. Without it, the compiler rejects the type with an error like: ```text invalid MLIR type: kgen.dtype ``` Use `__mlir_type.`!kgen.dtype`` instead. Here's a runnable example that declares MLIR-typed variables, assigns values with `__mlir_attr`, and converts them back to Mojo for printing: ```mojo def mlir_types_in_action(): var flag: __mlir_type.i1 = __mlir_attr.true var count: __mlir_type.index = __mlir_attr.`0 : index` # Convert back to Mojo types to print print(Bool(flag)) # True print(Int(mlir_value=count)) # 0 ``` ### Bracket syntax Use bracket syntax when you need to build a type from values known at compile time. The compiler splices Mojo expressions into an MLIR type string. The following list builds a single MLIR type string. It alternates between backtick literals (copied as-is) and Mojo expressions (inserted as MLIR text): ```mojo # From SIMD: build storage type from dtype and size parameters. # For SIMD[DType.float32, 4], produces: !kgen.simd<4, f32> comptime _mlir_type = __mlir_type[ `!kgen.simd<`, Self.size._mlir_value, `, `, Self.dtype._mlir_value, `>` ] # From Pointer: produces, for example, !kgen.pointer comptime _mlir_type = __mlir_type[`!kgen.pointer<`, Self.T, `>`] # From Optional: produces, for example, !kgen.variant comptime _mlir_type = __mlir_type[ `!kgen.variant<`, Self.T, `, i1>` ] # Nested substitution: produces complex var complexInt: __mlir_type[ `complex<`, __mlir_type.i32, `>` ] ``` Bracket lists only accept positional operands. Using keyword operands produces a compile-time error. ### MLIR types in parameter lists You can use MLIR types as compile-time parameters for structs and functions. Dialect types like `!kgen.string` and `!kgen.dtype` appear here alongside builtin types. ```mojo # Parameter is a compile-time MLIR string (for example, "hello") struct StringLiteral[value: __mlir_type.`!kgen.string`]: # ... # Parameter is a compile-time dtype (for example, f32 or si64) def example[dtype: __mlir_type.`!kgen.dtype`](): # For dtype=f32, produces: !kgen.scalar var a: __mlir_type[`!kgen.scalar<`, dtype, `>`] # ... ``` ### Properties of raw MLIR types Raw MLIR types (not wrapped in a struct) are register-passable, trivially copyable, and trivially movable. They don't have methods or attributes and accessing `.field` on the type produces an error. ## `__mlir_attr` Use `__mlir_attr` to define MLIR attributes in Mojo. An MLIR attribute is a compile-time constant embedded in the IR. ### Dot and backtick syntax Use dot syntax for simple attribute names. Use backticks when you need full MLIR literal syntax: ```mojo # Boolean attributes __mlir_attr.true __mlir_attr.false # Built-in type shorthands used in return positions __mlir_attr.i1 __mlir_attr.index __mlir_attr.f16 __mlir_attr.f32 __mlir_attr.si32 # Typed constants (with backtick syntax) __mlir_attr.`0 : index` __mlir_attr.`42 : i17` __mlir_attr.`1 : si32` ``` MLIR attribute constants use MLIR literal syntax, not Mojo's. Binary (`0b1010`), octal (`0o17`), and hex (`0xFF`) prefixes aren't supported. Use decimal values instead. ```mojo # Dialect-specific attributes __mlir_attr.`#kgen.dtype.constant : !kgen.dtype` # DType constant for float32 __mlir_attr.`#index` # Signed less-than predicate __mlir_attr.`#pop` # Sequential consistency ordering __mlir_attr.`#kgen.simd<"nan"> : !kgen.scalar` # Float32 NaN constant ``` Here's a runnable example that uses MLIR attributes as constants in a computation. This computes an approximate circle area using integer arithmetic: ```mojo def circle_area_approx(radius: Int) -> Int: """Approximate area using integer math: pi ≈ 3.""" var r = radius.__mlir_index__() var r_squared = __mlir_op.`index.mul`(r, r) var pi: __mlir_type.index = __mlir_attr.`3 : index` var area = __mlir_op.`index.mul`(pi, r_squared) return Int(mlir_value=area) def main(): print(circle_area_approx(5)) # 75 print(circle_area_approx(10)) # 300 ``` ### Bracket syntax Use bracket syntax when you need to build an attribute from compile-time values. This follows the same rules as `__mlir_type`. The following list builds a single MLIR attribute string. It alternates between backtick literals (copied as-is) and Mojo expressions (inserted as MLIR text): ```mojo # String concatenation at compile time. # For "Hello" + "World", produces: # #pop.string_concat<"Hello","World"> : !kgen.string __mlir_attr[ `#pop.string_concat<`, self.value, `,`, rhs.value, `> : !kgen.string`, ] # Null pointer constant for a parameterized type. # For Pointer[Int, MutAnyOrigin], produces: # #interp.pointer<0> : !kgen.pointer __mlir_attr[`#interp.pointer<0> : `, Self._mlir_type] # Compile-time parameter expression. # For a=5, produces: #kgen.param.expr : index comptime new_lower = __mlir_attr[ `#kgen.param.expr : index` ] ``` ## `__mlir_op` Use `__mlir_op` to call MLIR operations directly from Mojo. This lets you use operations that Mojo doesn't expose yet, like hardware intrinsics and dialect-specific operations. ### Syntax Place compile-time parameters (attributes) in square brackets and runtime values (operands) in parentheses. Put the operation name in backticks: ```mojo __mlir_op.`dialect.operation`(operands) __mlir_op.`dialect.operation`[attributes](operands) ``` ### Operations with no attributes When an operation only needs operands (runtime values), call the operation directly: ```mojo # Boolean XOR __mlir_op.`pop.xor`(self._mlir_value, rhs._mlir_value) # Index addition __mlir_op.`index.add`(self._mlir_value, rhs._mlir_value) # Trap (no operands, no result) __mlir_op.`llvm.intr.trap`() ``` ### Operations with attributes An operation may need to pass attributes, the key-value pairs in square brackets before the operands (that is, the runtime values): ```mojo # Cast a pop scalar to a builtin i1 # (for example, !kgen.scalar → i1) __mlir_op.`pop.cast_to_builtin`[ _type=__mlir_type.i1 ](mlir_value) # Signed less-than comparison on two index values. Returns i1. __mlir_op.`index.cmp`[ pred=__mlir_attr.`#index` ](self._mlir_value, rhs._mlir_value) # Load a value from a pointer with atomic ordering. # Returns a value of the pointer's element type. __mlir_op.`pop.load`[ ordering=ordering.__mlir_attr(), _type=Self._mlir_type, ](ptr.address) ``` Here's a runnable example that combines comparisons with `pop.select` to clamp a value into a range: ```mojo def clamp(val: Int, low: Int, high: Int) -> Int: """Clamp val to [low, high] using MLIR comparisons and select.""" var v = val.__mlir_index__() var lo = low.__mlir_index__() var hi = high.__mlir_index__() # If val < low, use low. # index.cmp returns i1, but pop.select needs a !kgen.scalar. var too_low = __mlir_op.`pop.cast_from_builtin`[ _type=__mlir_type.`!kgen.scalar` ]( __mlir_op.`index.cmp`[pred=__mlir_attr.`#index`]( v, lo ) ) var result = __mlir_op.`pop.select`(too_low, lo, v) # If result > high, use high var too_high = __mlir_op.`pop.cast_from_builtin`[ _type=__mlir_type.`!kgen.scalar` ]( __mlir_op.`index.cmp`[pred=__mlir_attr.`#index`]( result, hi ) ) result = __mlir_op.`pop.select`(too_high, hi, result) return Int(mlir_value=result) def main(): print(clamp(15, 0, 10)) # 10 print(clamp(-5, 0, 10)) # 0 print(clamp(7, 0, 10)) # 7 ``` ### Special attributes Three attributes have special meaning to the compiler: | Attribute | Purpose | |---------------|----------------------------------------------------------------------| | `_type` | Sets the result type; pass `None` for an operation with no result | | `_properties` | Passes MLIR operation properties as a `DictionaryAttr` | | `_region` | References a named `__mlir_region` as a region argument | ### `_type` Most operations require an explicit result type: ```mojo # Single result type var i1Cast = __mlir_op.`index.castu`[ _type=__mlir_type.i1 ](idxConstant) ``` When an operation returns multiple values, assign them to a typed tuple: ```mojo # Returns the current source location as (line, column, filename). # _properties passes inline depth to the code generator. _ = __mlir_op.`kgen.source_loc`[ _type = ( __mlir_type.index, __mlir_type.index, __mlir_type.`!kgen.string` ), ]() ``` Operations that produce no result take `_type=None`: ```mojo # Fence after mbarrier initialization. Guarantees the barrier # object is fully constructed before any thread uses it. __mlir_op.`nvvm.fence.mbarrier.init`[_type=None]() ``` If the compiler can't infer the result type and you don't provide `_type`, you'll receive an error: `unable to infer result type from MLIR operation 'name'`. ### `_properties` Some MLIR operations store configuration in properties instead of attributes. Attributes are compile-time constants in Mojo. Properties store compile-time metadata on the operation. Pass them as a `DictionaryAttr`: ```mojo # Returns current source location as (line, column, filename). # _properties passes inline depth to the code generator. _ = __mlir_op.`kgen.source_loc`[ _type = ( __mlir_type.index, __mlir_type.index, __mlir_type.`!kgen.string` ), _properties = __mlir_attr.`{inlineCount = 1 : i64}`, ]() ``` Operations can mix attributes and properties in the same bracket list: ```mojo # 64-bit integer addition with "no signed wrap" overflow checking. # nsw means undefined behavior on signed overflow, enabling # optimizations. __mlir_op.`llvm.add`[ _type=__mlir_type.i64, _properties=__mlir_attr.`{ overflowFlags = #llvm.overflow }`, ](arg0, arg1) ``` As an example, NVVM operations often use the `operandSegmentSizes` property to describe which optional operands are present: ```mojo # Async bulk copy from global to shared cluster memory. # operandSegmentSizes: dst(1), src(1), size(1), mbar(1), # cache_hint(0), predicate(1). __mlir_op.`nvvm.cp.async.bulk.shared.cluster.global`[ _properties=__mlir_attr.`{ operandSegmentSizes = array }`, _type=None, ](dst, src, size, mbar, predicate) ``` In Mojo, you can only call registered MLIR operations. If you try to call one that isn't registered, the compiler will error with: `use of unregistered MLIR operation 'name'`. ## `__mlir_region` Use `__mlir_region` to define a block of MLIR code for an operation to run. A region is a block of code that an MLIR operation runs, similar to a loop body or callback. Unlike a closure, it doesn't implicitly capture variables. Some MLIR operations take a region as input. You define the block with `__mlir_region` and pass it to the operation using the `_region` attribute. A region is written as a named block with arguments and an indented body: ```mojo __mlir_region name(arg: type, ...): body ``` ### Basic usage Some MLIR operations accept a region argument, a block of code that the operation controls. You define the region with `__mlir_region` and connect it to the operation with the `_region` attribute. The following example uses `hlcf.loop`, an MLIR loop operation. It repeatedly runs the region body, passing the current iteration value as an argument. The region calls `hlcf.continue` with the next value: ```mojo comptime one = __mlir_attr.`1 : index` def structured_for_loop() -> __mlir_type.index: # Define the loop body as a region. The operation passes # the current iteration value as `i`. __mlir_region loop_body(i: __mlir_type.index): # Yield the next iteration value: i + 1 __mlir_op.`hlcf.continue`( __mlir_op.`index.add`(i, one) ) # Start at 0, run loop_body repeatedly, # return the final value. return __mlir_op.`hlcf.loop`[ _type=__mlir_type.index, _region=__mlir_attr.`"loop_body"`, ](__mlir_attr.`0 : index`) ``` The region arguments (`i` in this example) come from the operation that uses the region. Here, `hlcf.loop` passes the current loop value as `i`. The `_region` attribute takes the region name as a string. This loop runs indefinitely. To exit conditionally, wrap `hlcf.break` in `hlcf.if`. In practice, it's simpler to use Mojo's `for` and `while` loops for control flow. The following example uses a Mojo `while` loop for control flow and MLIR operations for the computation inside. The loop condition uses an MLIR comparison, and the body uses MLIR arithmetic to update the accumulator and counter: ```mojo def sum_to(end: Int) -> Int: """Mojo while loop with MLIR arithmetic and comparison.""" var acc: __mlir_type.index = __mlir_attr.`0 : index` var i: __mlir_type.index = __mlir_attr.`0 : index` var one: __mlir_type.index = __mlir_attr.`1 : index` # end.__mlir_index__() unwraps Mojo Int to raw __mlir_type.index while Bool(__mlir_op.`index.cmp`[ pred=__mlir_attr.`#index` ](i, end.__mlir_index__())): acc = __mlir_op.`index.add`(acc, i) i = __mlir_op.`index.add`(i, one) return Int(mlir_value=acc) def main(): print(sum_to(10)) # 45 print(sum_to(0)) # 0 print(sum_to(1)) # 0 print(sum_to(5)) # 10 ``` ### Multiple regions in one scope A function can define multiple regions, each with its own name. This example defines a region and passes it to `co.suspend`, an MLIR coroutine operation that suspends execution and later resumes by running the provided region: ```mojo @always_inline def _suspend_async[ body: def(AnyCoroutine) capturing -> None ](): # Runs when the coroutine resumes. # The operation passes the coroutine handle as `hdl`. __mlir_region await_body( hdl: __mlir_type.`!co.routine` ): body(hdl) # Signal that the await body is done __mlir_op.`co.suspend.end`() # Suspend the current coroutine, registering await_body # as the code to run when it resumes. __mlir_op.`co.suspend`[_region="await_body".value]() ``` Operations that accept multiple regions reference them by name. Each `__mlir_region` defines a single block. ### Region arguments Region arguments look like function arguments, but they don't support Mojo argument conventions like `ref`, `var`, or `mut`. The operation provides the argument values directly as raw MLIR values: ```mojo # Region arguments receive raw MLIR values from the enclosing # operation. Mojo conventions don't apply. __mlir_region my_region( x: __mlir_type.index, # Raw index from the operation y: __mlir_type.`!kgen.scalar`, # Raw f32 scalar ): # x and y are raw MLIR values, not Mojo types. # Wrap them (for example, Int(mlir_value=x)) to use # Mojo operations on them. # ... ``` ## Common dialects These dialect prefixes appear frequently in the stdlib and kernel code: | Prefix | Covers | |-----------|-----------------------------------------------------------| | `pop.*` | Mojo portable ops: arithmetic, casts, SIMD, pointers | | `index.*` | Index-typed arithmetic and comparisons | | `kgen.*` | Codegen primitives: structs, variants, parameters | | `lit.*` | Language-level ops: ownership, references, closures | | `llvm.*` | LLVM dialect: traps, inline assembly, pointer ops | | `nvvm.*` | NVIDIA GPU intrinsics: barriers, async copies, tensor ops | | `co.*` | Coroutine ops: suspend, resume, destroy, await | The `pop`, `kgen`, `co` and `lit` dialects are internal implementation details of the compiler and may change without notice. Built-in dialects, like `index`, are also available. ## Stdlib patterns The standard library uses inline MLIR in consistent patterns that show up across the codebase. These patterns will help you understand how to use the built-ins in your own code. They serve as a reference for common use cases. ### Wrapper structs The most common pattern: a Mojo struct wraps a raw MLIR type in a field called `_mlir_value`. The struct provides a Mojo-friendly interface; the field holds the actual MLIR representation. `Bool` wraps a single bit: ```mojo # Bool wraps a single MLIR bit. The struct provides Mojo-level # operators; the i1 field holds the actual hardware value. struct Bool: var _mlir_value: __mlir_type.`!kgen.scalar` # 1-bit integer: true or false def __init__(out self, value: __mlir_type.`!kgen.scalar`): self._mlir_value = value # Store the raw bit directly ``` When the storage type depends on struct parameters, define it as a `comptime` alias. For example, `SIMD` builds its type from `dtype` and `size`: ```mojo # SIMD builds its storage type at compile time from its parameters. # For SIMD[DType.float32, 4], _mlir_type produces: !kgen.simd<4, f32> struct SIMD[dtype: DType, size: Int]: comptime _mlir_type = __mlir_type[ `!kgen.simd<`, Self.size._mlir_value, `, `, Self.dtype._mlir_value, `>` ] var _mlir_value: Self._mlir_type # Parameterized SIMD vector ``` This pattern appears in `SIMD`, `Pointer`, `Tuple`, `Variant`, and `Optional`'s internal storage. Here's a complete, runnable wrapper struct. `Counter` wraps an MLIR index, exposes `increment` and `value` methods, and converts back to Mojo for printing: ```mojo struct Counter: """A simple counter backed by a raw MLIR index.""" var _mlir_value: __mlir_type.index def __init__(out self): self._mlir_value = __mlir_attr.`0 : index` def increment(mut self): var one: __mlir_type.index = __mlir_attr.`1 : index` self._mlir_value = __mlir_op.`index.add`( self._mlir_value, one ) def value(self) -> Int: return Int(mlir_value=self._mlir_value) def main(): var c = Counter() c.increment() c.increment() c.increment() print(c.value()) # 3 ``` ### Operations as methods Once a struct wraps an MLIR type, its methods delegate to MLIR operations. The `_mlir_value` field goes in, the result comes back, and the struct re-wraps it: ```mojo # From Bool: operators delegate to pop operations on the raw i1. # XOR with true flips the bit: ~False → True, ~True → False def __invert__(self) -> Bool: return __mlir_op.`pop.xor`( self._mlir_value, __mlir_attr.true ) # Bitwise AND on the two underlying i1 values def __and__(self, rhs: Bool) -> Bool: return __mlir_op.`pop.and`( self._mlir_value, rhs._mlir_value ) ``` For `Int`, the `index` dialect operations produce raw index values. `Int(mlir_value=...)` wraps them back: ```mojo # From Int: add two raw index values, wrap the result back into Int def __add__(self, rhs: Int) -> Int: return Int( mlir_value=__mlir_op.`index.add`( self._mlir_value, rhs._mlir_value ) ) # Signed less-than comparison, returns i1 (auto-wraps to Bool) def __lt__(self, rhs: Int) -> Bool: return __mlir_op.`index.cmp`[ pred=__mlir_attr.`#index` ](self._mlir_value, rhs._mlir_value) ``` Here's the `Counter` struct from the previous example extended with an `__add__` operator, showing how the pattern applies to custom types: ```mojo struct Counter: """A counter with addition, backed by a raw MLIR index.""" var _mlir_value: __mlir_type.index def __init__(out self): self._mlir_value = __mlir_attr.`0 : index` def __init__(out self, *, mlir_value: __mlir_type.index): self._mlir_value = mlir_value def increment(mut self): var one: __mlir_type.index = __mlir_attr.`1 : index` self._mlir_value = __mlir_op.`index.add`( self._mlir_value, one ) def __add__(self, rhs: Counter) -> Counter: return Counter( mlir_value=__mlir_op.`index.add`( self._mlir_value, rhs._mlir_value ) ) def value(self) -> Int: return Int(mlir_value=self._mlir_value) def main(): var a = Counter() a.increment() # 1 a.increment() # 2 var b = Counter() b.increment() # 1 var c = a + b print(c.value()) # 3 ``` --- ## Mojo identifiers, keywords, and conventions reference {/* VERIFIED: TokenKinds.def, Lexer.cpp, Signatures.h, Signatures.cpp, ExprNode.h, ParserExprs.cpp, ParserStmts.cpp, ParserBase.h */} {/* In TokenKinds.def, `_` is declared with TOK_KEYWORD rather than TOK_PUNCTUATION. This is an internal classification used by the lexer; for the language surface, `_` is a regular identifier character (see the identifier grammar below). Do not document `_` as a keyword. */} Every Mojo source file is built from tokens. This page covers the tokens you choose (identifiers), the tokens the language reserves (keywords), and the context-sensitive tokens that control how values are passed and bound (conventions). ## Identifiers Every time you declare a variable, define a function, or create a struct, you give it a name. That name is an *identifier*. Identifiers are how you refer to things in your code: `count` in `var count = 0`, `Point` in `struct Point`, `greet` in `def greet()`. This section covers what makes a valid identifier. ### Regular identifiers A regular identifier starts with a letter or underscore, followed by any combination of letters, digits, and underscores: ```text identifier → [a-zA-Z_][a-zA-Z0-9_]* ``` Such as: ```mojo foo _private MyStruct basic_value ``` Identifiers are case-sensitive. `MyStruct` and `mystruct` are different names: ```mojo var MyStruct = 1 var mystruct = 2 assert_equal(MyStruct, 1) # passes assert_equal(mystruct, 2) # passes ``` ### Escaped identifiers An *escaped identifier* is enclosed in backticks. Backticks allow any characters except vertical whitespace and backticks themselves: ```mojo `struct` # Use a keyword as a name `日本語の変数` # Non-ASCII identifier `my value` # Spaces in a name ``` Escaped identifiers are useful when calling into external code that uses a Mojo keyword as a name, or when writing identifiers in natural language. Empty backtick identifiers are not allowed. ## Keywords *Keywords* are reserved words with fixed meaning. They cannot be used as ordinary identifiers (use an escaped identifier if you need to). ### Control flow These keywords control which code runs and in what order. | Keyword | Purpose | |------------|-----------------------------------------| | `if` | Conditional execution | | `elif` | Additional condition in an `if` chain | | `else` | Default branch in conditionals or loops | | `for` | Iteration loop | | `while` | Conditional loop | | `break` | Exits the innermost loop | | `continue` | Skips to the next loop iteration | | `pass` | No-op placeholder statement | | `return` | Returns from a function | | `with` | Context manager statement | ### Error handling These keywords structure error propagation and recovery. | Keyword | Purpose | |-----------|-------------------------------------------------------| | `try` | Begins an error-handling block | | `except` | Error handler clause | | `finally` | Always-execute clause in a `try` block | | `raise` | Raises an error | | `assert` | Aborts if a condition is false (gated by `-D ASSERT`) | ### Declarations These keywords introduce functions, types, and bindings. | Keyword | Purpose | |----------|------------------------------------------| | `def` | Function declaration | | `lambda` | Anonymous single-expression function | | `struct` | Struct type declaration | | `trait` | Trait declaration | | `var` | Scoped variable binding | | `ref` | Scoped reference binding | ### Keyword operators Five operators are spelled as words rather than symbols. Symbolic operators (`+`, `-`, `*`, `^`, `//`, etc.) are punctuation, not keywords. | Keyword | Purpose | Keyword | Purpose | |---------|---------------|---------|-----------------| | `and` | Logical AND | `or` | Logical OR | | `not` | Logical NOT | `in` | Membership test | | `is` | Identity test | | | ### Imports These keywords control module imports. | Keyword | Purpose | |----------|------------------------------------------| | `import` | Imports a module | | `from` | Selective import from a module | | `as` | Aliasing in imports and `except` clauses | ### Compile-time | Keyword | Purpose | |------------|--------------------------------| | `comptime` | Forces compile-time evaluation | ### Literal keywords These keywords are also literals. They produce a value directly. | Keyword | Value | Keyword | Value | |---------|---------------------------------|---------|--------------------| | `True` | boolean true | `False` | boolean false | | `None` | Absence of a value (`NoneType`) | `Self` | The enclosing type | ### Case sensitivity All keywords are case-sensitive: - `True` is a keyword; `true` is not. - `None` is a keyword; `none` is not. - `Self` is a keyword; `self` is a conventional argument name, not a keyword. ## Conventions *Conventions* tell the compiler how values are passed and how bindings are created. Convention names aren't reserved, so existing Python code that uses these names won't break, but in Mojo signatures they have fixed meaning. **Argument conventions** appear before argument names in function signatures. They control ownership and mutability: {/* markdownlint-disable MD013 */} | Convention | Role | Meaning | |------------|----------------------|-------------------------------------------------------------| | (`imm`) | Argument | Immutable reference to an existing value (default behavior) | | `mut` | Argument | Mutable reference to an existing value | | `out` | Argument | Returns a value without a return arrow | | `deinit` | Argument | Destructive transfer; end of a value's lifecycle | | `var` | Argument or variable | Independent mutable owned copy of the value | | `ref` | Argument or variable | Reference that doesn't own the value | {/* markdownlint-enable MD013 */} `var` and `ref` also appear in **variable declarations**, where `var` creates a scoped mutable variable and `ref` creates a scoped reference binding: ```mojo struct CountingTool: var value: Int def __init__(out self): # out: self is the return value self.value = 0 def increment(mut self): # mut: modifies self in place self.value += 1 var count = 0 # var creates a scoped mutable variable var data: List[Int] = [1, 2, 3] ref view = data # ref binds a reference, no copying ``` A `self` argument without a convention is an immutable reference. Modifying it requires `mut`: ```mojo struct CountingTool: # continuing from above... def get(self) -> Int: self.value += 1 # Error: self is immutable return self.value ``` `out` declares the return value by name. It can't be combined with `->`: ```mojo def make_point(out result: Point): # OK result = Point(0, 0) def make_point(out result: Point) -> Point: # Error: function cannot have result = Point(0, 0) # both an 'out' argument and # an explicit result type ``` `raises` and `where` also have fixed meaning in declarations. `raises` declares that a function can raise errors. `where` introduces a constraint clause at the end of a declaration: ```mojo def validate(value: Int) raises: if value < 0: raise Error("must be non-negative") def process[T: Copyable](value: T) where conforms_to(T, Sized): # T must implement Sized to be used here ``` --- ## Mojo lambda expressions reference {/* Compiler verified: 2026-08-05, Mojo 1.0.0.dev0 (modular b6e7701fac1) Last updated: 2026-08-05 (new page); 2026-08-06 (polish, clarity) VERIFIED: ParserExprs.cpp (parseLambda, lambda_expr grammar comment, kw_lambda in canParseAtom, unparenthesized-args diagnostic), ExprNodes.h / ExprNodes.cpp / ExprNodePrinters.cpp (LambdaNode), DeclResolution.cpp (LambdaNode::emitIR: isThin test = bodyCaptures.values.empty() && node->captures.empty() && !node->captureAllByConvention, plus the input-param singleton test; promoteClosure fold; emitClosureInstance fallback; "cannot use a capturing lambda" diagnostics), Signatures.cpp (parseCaptureList, ParsedCaptureList, captureAllByConvention), mblib2to3/Grammar.txt (lambdef, old_lambdef), mblack/nodes.py (space after `lambda`). All runnable examples on this page execute in docs/code/reference/lambda-expressions/tests.mojo; the rejected forms were each confirmed against the compiler individually. */} A *lambda* is an anonymous, single-expression function. It has no name, its body is a single expression, and it doesn't use `return`. Lambda expressions are commonly passed to other functions as arguments, making them useful for higher-order programming, callbacks, event handlers, and other localized behavior. In Mojo, lambda expressions are part of the function declaration family: ```mojo def main(): var inc = lambda (x: Int) -> Int: x + 1 print(inc(4)) # 5 ``` This lambda is equivalent to the following function declaration: ```mojo def inc(x: Int) -> Int: return x + 1 print(inc(4)) # 5 ``` Lambdas complement `def` functions. Use `def` for named, reusable functions and lambdas for short, inline behavior. :::caution Lambda expressions are under active development. Return-type inference isn't implemented. If you omit the return type, the lambda returns `None`, even when its body produces a value. The rules on this page describe Mojo's current behavior. ::: ## Syntax Lambda expressions use a compact syntax. Most parts are optional, depending on what the lambda captures, accepts, and returns. Every lambda includes the `lambda` keyword, a body expression, and the `:` that introduces it: ```text lambda [[parameter-list]] [(argument-list)] [effects] [{capture-list}] [-> ResultType] : expression ``` The simplest lambda takes no arguments, captures nothing, and returns `None`: ```text lambda: None ``` Each part follows the same convention as standard functions: ```mojo def main(): # Fully explicit. Uses an empty "no-capture" capture list `{}` var a = lambda (x: Int) {} -> Int: x + 1 # Parameterized var b = lambda [T: Intable](x: T) -> Int: Int(x) + 1 var y = 1 # Capture list omitted. `y` defaults to `imm` var c = lambda (x: Int) -> Int: x * 2 + y # Return type omitted (`None`) var list: List[Int] = [1] var d = lambda (x: Int) {mut list}: list.append(x) # Arguments and return type omitted. Mutable capture var e = lambda {mut list}: list.append(0) print(a(4), b(4)) # 5 8 ``` ## Arguments Each argument must appear in parentheses and have a type. Types can be concrete (`String`) or parameterized (`T`, `Self.U`): ```mojo # Concrete argument var hello = lambda (x: String) {} -> String: "Hello, " + x # Parameterized argument var inc = lambda [T: Intable](x: T) -> Int: Int(x) + 1 ``` Omit the argument list for lambdas that don't take arguments: ```mojo var no_args = lambda -> Int: 42 ``` ### Argument conventions Arguments use the same conventions as functions. The default convention is `imm`, which captures an immutable reference: ```mojo def main(): var read_arg = lambda (x: Int) {} -> Int: x + 1 # Same as: var read_arg = lambda (imm x: Int) {} -> Int: x + 1 var own_arg = lambda (var x: Int) {} -> Int: x + 1 var mut_arg = lambda (mut x: Int) {}: x.__iadd__(1) var list: List[Int] = [1, 2, 3] mut_arg(list[0]) print(list) # [2, 2, 3] ``` ### Variadic arguments Lambda expressions support `*args` and `**kwargs`, separately or together. `**kwargs` packs into an `OwnedKwargsDict`, so declare it with `var`: ```mojo def main(): var count = lambda (*args: Int) {} -> Int: len(args) var named = lambda (var **kwargs: Int) {} -> Int: len(kwargs) print(count(10, 20, 30)) # 3 print(named(a=1, b=2)) # 2 ``` ## Return types When omitted, return types default to `None`: ```mojo lambda: 5 # Error: can't convert IntLiteral to None lambda (x: Int) {}: x + 1 # Error: can't convert Int to None ``` ## Lambda closures and capture lists A lambda becomes a *closure* when it carries state from its enclosing scope or binds the parameters it declares at each call site. Lambda capture lists use the same [conventions](/docs/reference/closure-declarations/#capture-conventions) as nested `def` closures: `imm`, `mut`, `ref`, `var`, plus copyable and movable. A lambda becomes a closure under these circumstances: - The lambda body references a value from the enclosing scope. ```mojo var z = 10 var f = lambda (x: Int) -> Int: x + z # `z` is captured print(f(5)) # 15 ``` In the absence of an explicit capture list, the default capture convention used here is an immutable reference (`imm`). - The lambda body uses an explicit capture convention. ```mojo var list: List[Int] = [1, 2, 3] var f = lambda (x: Int) {mut list}: list.append(x) # `list` is captured f(10) print(list) # [1, 2, 3, 10] ``` Using `{mut}` produces the same result, but `{mut list}` is more precise. It limits captures to `list`. `{mut}` captures every outer value used in the body. Explicitly naming captures turns accidental references into errors instead of silent captures. - The lambda declares its own parameter list. ```mojo # N is lambda-owned, bound at each call var f = lambda [N: Int](x: Int) {} -> Int: x + N print(f[5](3)) # 8 ``` Parameters declared by an enclosing scope are compile-time substituted, not captured: ```mojo def total_as_ints[T: Intable & Copyable](args: List[T]) -> Int: var to_int: def(v: T) thin -> Int = lambda (v: T) -> Int: Int(v) var total = 0 for ref a in args: total += to_int(a) return total def main(): print(total_as_ints([1.5, 2.5, 3.9])) # 6 ``` An empty capture list (`{}`) means "capture nothing." It excludes the default `imm` convention, so using a variable from an enclosing scope is an error: ```mojo var z = 10 var f = lambda (x: Int) {} -> Int: x + z # Error: z isn't captured ``` Any explicit capture convention makes a lambda a closure, even when it captures nothing. For example, `lambda (x: Int) {imm} -> Int: x + 1` is a closure, while the same lambda with the capture list omitted is *thin*. ## Thin lambdas {#thin} A lambda that isn't a closure is `thin`. To be thin, a lambda captures nothing, uses no explicit capture conventions, and doesn't declare its own parameters. Thin lambdas can be: - Used as a thin function pointer, including `abi("C")` callbacks. - Passed as a thin function-type parameter. - Bound to a symbol with `comptime`. - Returned from a function. - Stored in a struct field. - Used as a default argument or parameter value. Thin matters when a lambda must outlive the scope that created it or is needed at compile time. ### C ABI boundaries Thin lambdas can cross C ABI boundaries because they don't carry runtime state: ```mojo var fp = lambda (a: Int32, b: Int32) abi("C") -> Int32: a + b print(fp(1, 2)) # 3 ``` The `abi("C")` effect must appear on the lambda declaration. It's not enough to type the variable. If you choose to use explicit typing, the type must *also* carry the `abi("C")` effect. ### Compile-time use You can call any lambda directly. You can also pass any lambda as a runtime argument to higher-order functions with function-shaped infer-only parameter types: ```mojo def hof[T: def(x: Int) -> Int, //](f: T): # ... ``` Compile-time parameters are different. Unless a parameter is typed as `thin`, you can't pass lambdas at compile time. When it *is*, the lambda must be thin. Mojo can execute thin lambdas at compile time, and assign the result to a comptime name: ```mojo def main(): comptime whole = (lambda (x: Int) {} -> Int: x * 2)(21) print(whole) # 42 ``` ## Effects Place lambda effects after the argument list and before the capture list: ```mojo def apply_raising(f: def(x: Int) raises thin -> Int, arg: Int) raises -> Int: return f(arg) def main() raises: print(apply_raising(lambda (x: Int) raises -> Int: x + 1, 2)) # 3 ``` Every lambda body is a single expression and can't contain a `raise` statement. Lambdas only raise by calling something else that raises. Declaring `raises` allows the lambda to propagate exceptions, not raise them. ## Nesting Each lambda body can contain another lambda expression. The inner one captures the outer one's arguments through its own capture list, and references the outer one's parameters directly: ```mojo def main(): var f = lambda (x: Int) {} -> Int: ( lambda (y: Int) {imm x} -> Int: y + x )(3) print(f(6)) # 9 ``` ## Restrictions Lambda expressions have the following restrictions: - **Single expression**: The body is one expression. There's no `return`, no statement body, and no multi-statement form. - **No return-type inference**: An omitted return type is `None`, not a solved type. - **Arguments need types**: There's no argument-type inference from the use site. - **No `thin` in the signature**: `thin` applies to function *types*, not to lambda declarations. So long as the lambda doesn't declare any parameters that must be supplied from call sites, adding the `{}` capture list ensures the lambda is thin. ## Errors {/* markdownlint-disable MD013 */} | Compiler complaint | Trigger | |-----------------------------------------------|-------------------------------------------------------------------------------------| | Can't convert value to `None` in return value | Returns a value from a lambda with no return type | | Could not infer capture convention | Excludes capture conventions (`{}`) but references a value from the enclosing scope | | Mutating method on an immutable value | Mutation through an `imm` capture | | Capturing lambda in comptime initializer | A lambda closure bound to a `comptime` name | | Capturing lambda in type parameter | A lambda closure passed to a type parameter | | Capturing lambda in default parameter | A lambda closure used as a default parameter value | | Can't implicitly convert to a `thin` type | A lambda closure passed where a thin function is required | {/* markdownlint-enable MD013 */} --- ## Mojo literals reference A *literal* is a value written directly in source code: `42`, `"hello"`, `True`. Literals produce values without reading variables or calling functions. Each section below covers one literal type, its syntax, and any rules the lexer enforces. :::note Materialization makes a compile-time value available at runtime. Integer, floating-point, and string literals are implicitly materialized to their respective runtime types (Int, Float and String). ::: ## Integer literals *Integer literals* represent whole numbers in four bases: ```mojo 42 # Decimal 0xFF # Hexadecimal (0x or 0X prefix) 0o52 # Octal (0o or 0O prefix) 0b101010 # Binary (0b or 0B prefix) ``` Integer literals follow these lexical rules: ```text integer → decinteger | bininteger | octinteger | hexinteger decinteger → nonzerodigit ("_" | digit)* | "0"+ ("_" | "0")* bininteger → "0" ("b" | "B") ("_" | bindigit)+ octinteger → "0" ("o" | "O") ("_" | octdigit)+ hexinteger → "0" ("x" | "X") ("_" | hexdigit)+ ``` Integer literals are always non-negative. `-1024` is the unary negation operator `-` applied to the literal `1024`. Underscores can appear between digits for readability. Mojo is more permissive than Python here. Consecutive and trailing underscores are allowed: ```mojo 1_000_000 # Readable grouping 1__000_ # Also valid (consecutive and trailing underscores OK) ``` Leading zeros in decimal literals are not allowed. Use the `0o` prefix for octal: ```mojo 0123 # Error: leading zeros in decimal integer literals are not permitted 0o123 # OK: octal ``` A base prefix must be followed by at least one digit: ```mojo 0x # Error: no digits specified for hex literal 0b # Error: no digits specified for binary literal 0o # Error: no digits specified for octal literal ``` ## Floating-point literals *Floating-point literals* represent numbers with a fractional or exponent part: ```mojo 1.0 3.14159 .5 # Fraction only (no integer part) 2. # Integer part with decimal point 2.5e-3 # With exponent 1E10 # Capital E works too ``` Floating-point literals follow these lexical rules: ```text floatnumber → pointfloat | exponentfloat pointfloat → digitpart? fraction | digitpart "." exponentfloat → (digitpart | pointfloat) exponent fraction → "." digitpart exponent → ("e" | "E") ("+" | "-")? digitpart digitpart → digit ("_" | digit)* ``` Floating-point literals are always non-negative. `-3.14` is the unary negation operator `-` applied to the literal `3.14`. When included, an exponent marker (`e` or `E`) must be followed by at least one digit: ```mojo 2.5e # Error: expecting a digit after the exponent 2.5e- # Error: expecting a digit after the exponent 2.5e-3 # OK ``` Underscores in floating-point literals work as they do in integers. Place them anywhere that enhances readability: ```mojo 1_000.000_5 ``` ## String literals *String literals* represent text values. Mojo supports single and double quotes, and a triple-quote form for multi-line strings: ```mojo "Hello" 'world' """Multi-line string """ # Includes final newline '''Also multi-line''' # Includes 4 spaces at the start of the second line ``` Triple-quoted strings include any newlines literally. A backslash at the end of a line suppresses the newline, joining the next line directly: ```mojo """\ This string has no leading newline.""" ``` String literals on adjacent lines are joined into a single string. This works on one line or across lines when the continuation is indented: ```mojo var x = "Hello, " "World" # "Hello, World" var y = "line one " "line two" # "line one line two" (indented continuation) ``` Prefix with `r` or `R` to create a *raw string* that disables escape processing: ```mojo r"C:\path\to\file" # Backslashes treated literally ``` ### Escape sequences Mojo recognizes these escape sequences in non-raw string literals: | Sequence | Meaning | Sequence | Meaning | |----------|-----------------------------------|--------------|-----------------------------------| | `\\` | Backslash | `\a` | Bell | | `\"` | Double quote | `\b` | Backspace | | `\'` | Single quote | `\f` | Form feed | | `\n` | Newline | `\v` | Vertical tab | | `\r` | Carriage return | `\xHH` | Hex value (exactly 2 hex digits) | | `\t` | Tab | `\0`–`\377` | Octal value (1–3 octal digits) | | `\uHHHH` | Unicode code point (4 hex digits) | `\UHHHHHHHH` | Unicode code point (8 hex digits) | Mojo source files are UTF-8. String literals may contain non-ASCII characters directly. ```mojo var wave = "👋" ``` Non-ASCII characters can also be written as Unicode hex escapes: ```mojo var wave = "\U0001F44B" # 8-digit hex escape, 👋 var euro = "\u20AC" # 4-digit hex escape, € ``` - `\uHHHH` accepts code points from U+0000 to U+FFFF - `\UHHHHHHHH` accepts the full Unicode range, U+0000 to U+10FFFF Both forms reject surrogate code points (U+D800 to U+DFFF), which are reserved for UTF-16 encoding. Code points above U+FFFF require `\U`, not a UTF-16 surrogate pair. ## T-string literals *T-string literals* support expression interpolation using `{}`: ```mojo var name = "World" var greeting_template = t"Hello, {name}!" # "Hello, World!" var result_template = t"1 + 1 = {1 + 1}" # "1 + 1 = 2" ``` Expressions inside `{}` are evaluated at runtime. Adjacent t-string literals are joined, just like regular string literals. To use them as strings except in print statements, cast them to `String`: ```mojo var name = "Alice" var greeting = t"Hello, {name}!" # Type is T-string print(greeting) # Prints "Hello, Alice!" var greeting_str = String(greeting) # Convert to regular String ``` T-strings can be triple-quoted and combined with the raw prefix (any case combination of `r`/`R` and `t`/`T`, in either order): ```mojo t""" Hello, {name}! """ rt"Path: {base}\subdir" # Raw t-string: backslashes are literal ``` Use `{{` and `}}` to include literal braces in a t-string: ```mojo t"Use {{braces}} in t-strings" # "Use {braces} in t-strings" ``` T-strings can be nested. An interpolation expression can itself contain t-strings, up to 20 levels deep. ```mojo var name = "world" var greeting = t"Hello, {t"dear {name}"}!" print(greeting) # "Hello, dear world!" ``` ## Boolean literals `True` and `False` represent boolean truth values. ```mojo var x = True var y = False ``` ## None literal `None` represents the absence of a value. It's the only value of type `NoneType`. ```mojo var x: NoneType = None ``` A function without an explicit return type returns `None`. These two declarations are equivalent: ```mojo def greet(): print("hello") def greet() -> None: print("hello") ``` ## Self literal `Self` refers to the enclosing type inside a struct or trait definition: ```mojo from std.math import sqrt @fieldwise_init struct Point: var x: Float64 var y: Float64 @staticmethod def create() -> Self: # Self refers to Point return Self(0.0, 0.0) def distance(self) -> Float64: # self is an argument name, not Self return sqrt(self.x ** 2 + self.y ** 2) ``` `Self` (capital S) is a keyword that refers to the type. `self` (lowercase) is a conventional argument name for the instance. ## Discard pattern The underscore `_` discards a value in an assignment: ```mojo _, var y = get_pair() # Ignore the first element ``` ## Ellipsis literal `...` marks a trait method as required. Conforming types must provide their own implementation. It's only valid inside trait definitions: ```mojo trait Drawable: def draw(self) -> None: ... # Required: conforming types must implement ``` `...` and `pass` aren't interchangeable. `pass` is a no-op statement that provides an empty body. `...` is a requirement marker that means "you must implement this." --- ## Mojo numeric types reference Mojo's numeric primitives are built on `SIMD` vectors. Every fixed-width numeric type is a one-element `SIMD` called a `Scalar`. The `DType` specifies the kind of values stored in a `SIMD` vector, such as `int`, `uint`, `float32`, `int64`, or `uint8`. Mojo provides sized and unsized integer types, floating-point types, and `Byte` for raw byte data. ## `SIMD` {#simd} `SIMD` stands for "Single Instruction, Multiple Data". It lets the CPU operate on multiple values at once using a single instruction. A `SIMD` value stores one or more values of the same type in a fixed-size vector. The number of values is called the *width*, and it must be a power of two. The width is part of the type. For example, `SIMD[.float32, 4]` is a vector of four 32-bit floats. `SIMD[.int8, 16]` is a vector of sixteen 8-bit integers. When the first `SIMD` parameter expects a `DType` value, you can write `.float32` instead of `DType.float32`. The same shorthand works in `.cast[.int32]()` and other APIs that take a `DType` argument. When a `SIMD` value holds one value, it behaves like a scalar. When it holds several, operations apply to all values at once: ```mojo var v = SIMD[.float32, 4](1.0, 2.0, 3.0, 4.0) var doubled = v * 2.0 # All four elements doubled print(doubled) # [2.0, 4.0, 6.0, 8.0] ``` Modern CPUs can process 4, 8, 16, or more values in parallel with SIMD, which can significantly improve performance over scalar operations. :::note `SIMD` has a hard limit of 2**15 (32768) elements. This is a compile-time limit, not a runtime one. In practice, the usable width is much smaller and depends on the hardware. For example, `SIMD[.float32, 4]` fits in a 128-bit register, while `SIMD[.float32, 16]` requires 512 bits, which matches or exceeds the width of most SIMD registers. Always benchmark to find the optimal width for your workload and target hardware. ::: ### Element access Read and write individual elements by index ("*lane*"): ```mojo v[0] # Read element 0 → Scalar[.float32] v[0] = 5.0 # Write element 0 ``` ### Operations Arithmetic, comparison, and bitwise operations apply to all elements at once: ```mojo var a = SIMD[.float32, 4](1.0, 2.0, 3.0, 4.0) var b = SIMD[.float32, 4](5.0, 6.0, 7.0, 8.0) var sum = a + b # [6.0, 8.0, 10.0, 12.0] var prod = a * b # [5.0, 12.0, 21.0, 32.0] ``` Reductions combine all elements into a single value: ```mojo a.reduce_add() # 10.0 a.reduce_max() # 4.0 a.reduce_min() # 1.0 ``` Casting converts each element to a different numeric type. The number of elements stays the same, even when the target type is wider or narrower: ```mojo var a = SIMD[.float32, 4](1.0, 2.0, 3.0, 4.0) var ints = a.cast[.int32]() # [1, 2, 3, 4] var wide = a.cast[.float64]() # 4 × Float64 var tiny = a.cast[.float16]() # 4 × Float16 ``` Clamping restricts elements to a range. Both bounds are inclusive, so the result can equal the bounds: ```mojo # max(min(self, upper_bound), lower_bound) a.clamp(1.5, 3.5) # [1.5, 2.0, 3.0, 3.5] ``` `min()` and `max()` are free functions, not methods: ```mojo min(a, b) # Element-wise minimum max(a, b) # Element-wise maximum ``` ## `Scalar` {#scalar} A `SIMD` with one element is called a `Scalar`. Every fixed-width numeric name in Mojo is a `Scalar` alias: ```mojo # These are all the same type var a: Scalar[.float32] = 3.14 var b: Float32 = 3.14 var c: SIMD[.float32, 1] = 3.14 ``` When you write `Float32`, you're writing `Scalar[.float32]`, which is `SIMD[.float32, 1]`. ## `DType` specifications {#dtype} `DType` names the kind of values stored in a `SIMD` vector, such as `float32`, `int64`, or `uint8`. A `DType` doesn't store data. It tells `SIMD` how to interpret each element and which operations to use: ```mojo # DType selects a number kind, such as 32-bit float or 8-bit integer var x: SIMD[.float32, 4] = # ... # four 32-bit floats var y: SIMD[.int8, 16] = # ... # sixteen 8-bit ints ``` Use `DType` to write functions that work across numeric kinds: ```mojo # Double a value. The cast is required because the parameterized type # parameter can't be used directly with the literal `2`. def double[T: DType](x: Scalar[T]) -> Scalar[T]: return x * UInt8(2).cast[T]() ``` ### Integer DType specifications In contexts that expect a `DType` value, write the contextual form (for example `.int32` instead of `DType.int32`). The tables list every member: | Signed | Width | Unsigned | Width | |-----------|---------|--------------|---------| | `.int8` | 8-bit | `.uint8` | 8-bit | | `.int16` | 16-bit | `.uint16` | 16-bit | | `.int32` | 32-bit | `.uint32` | 32-bit | | `.int64` | 64-bit | `.uint64` | 64-bit | | `.int128` | 128-bit | `.uint128` | 128-bit | | `.int256` | 256-bit | `.uint256` | 256-bit | | `.int` | Machine | `.uint` | Machine | ### Floating-point DType specifications | Value | Selects | |--------------------|----------------------------| | `.float16` | 16-bit IEEE half | | `.bfloat16` | 16-bit brain float | | `.float32` | 32-bit IEEE single | | `.float64` | 64-bit IEEE double | | `.float8_e4m3fn` | 8-bit (4-exp, 3-mantissa) | | `.float8_e4m3fnuz` | 8-bit, unsigned zero | | `.float8_e5m2` | 8-bit (5-exp, 2-mantissa) | | `.float8_e5m2fnuz` | 8-bit, unsigned zero | | `.float8_e8m0fnu` | 8-bit (8-exp, no mantissa) | | `.float4_e2m1fn` | 4-bit (2-exp, 1-mantissa) | ### Other DType specifications | Value | Selects | |---------|-----------------| | `.bool` | Boolean (1-bit) | ## Integers ### The unsized `Int` type {#int} `Int` is Mojo's default integer. When you write `var x = 42`, you assign an `Int`. It's the type behind loop counters, collection indices, and `len()` results: ```mojo def main(): var a: Int = 42 comptime a_type = reflect[type_of(a)].name() print("a:", a_type) # a: SIMD[DType.int, 1] ``` `Int` matches the hardware's native word size. Under the hood it wraps the machine's index register directly, which is why it's the natural choice for counting and addressing. `Int` is 64-bit on most platforms today, but that isn't guaranteed. Code that depends on a specific width should use a sized type. `Int` is equivalent to `Scalar[.int]` and `SIMD[.int, 1]`. ### Integer-type bounds `Int` exposes its bounds as compile-time constants: | Constant | Value | |----------------|---------------------------------| | `Int.MAX` | Maximum representable value | | `Int.MIN` | Minimum representable value | ```mojo print(Int.MIN) # -9223372036854775808 print(Int.MAX) # 9223372036854775807 ``` All integer types offer `MAX` and `MIN` as well: | Constant | Value | |----------------------|-----------------------------| | `.MAX` | Maximum representable value | | `.MIN` | Minimum representable value | For example: ```mojo print(UInt.MIN) # 0 print(UInt.MAX) # 18446744073709551615 print(UInt8.MAX) # 255 print(Int8.MIN) # -128 print(UInt32.MAX) # 4294967295 print(Int32.MIN) # -2147483648 print(SIMD[.int16, 1].MIN) # -32768 ``` ### `UInt` {#uint} `UInt` is a machine-width unsigned integer: ```mojo def main(): var b: UInt = 42 comptime b_type = reflect[type_of(b)].name() print("b:", b_type) # b: SIMD[DType.uint, 1] ``` ### Sized integer types Sized integer types have a declared width that stays the same on every platform. | Signed | Width | Unsigned | Width | |----------|---------|-----------|---------| | `Int8` | 8-bit | `UInt8` | 8-bit | | `Int16` | 16-bit | `UInt16` | 16-bit | | `Int32` | 32-bit | `UInt32` | 32-bit | | `Int64` | 64-bit | `UInt64` | 64-bit | | `Int128` | 128-bit | `UInt128` | 128-bit | | `Int256` | 256-bit | `UInt256` | 256-bit | Each is an alias for a one-element `SIMD`. For example, `Int32` is `Scalar[.int32]`, which is `SIMD[.int32, 1]`. The unsigned types follow the same pattern. **Using sized vs unsized integers**: - Use `Int` and `UInt` for counts, indices, loop bounds, and general-purpose math. It's what the standard library expects and returns. - Use sized integers when width matters: file layouts, pixel data, hardware registers, or any context where the number of bits is part of the contract. - Use named types for scalar work and `SIMD` when you need vectors. ```mojo var general = 42 # Int (machine width) var small: UInt8 = 255 var large: Int64 = -9_000_000_000 var pair = SIMD[.uint32, 2](10, 20) # a 2-element vector ``` ### `Byte` {#byte} `Byte` is another name for `UInt8`: ```mojo var buf: List[Byte] = [0x48, 0x65, 0x6C, 0x6C, 0x6F] ``` Use `Byte` when the data represents raw bytes rather than small numbers. It's the element type used in many I/O and memory interfaces. ## Floating point types Mojo doesn't provide a `Float` type analogous to `Int`. Instead it provides numerous fixed-width floating-point types. Each is an alias for a one-element `SIMD`: | Type | Bits | Standard | What it is | |-------------------|-----------|-------------------|-----------------------------------------------| | `Float16` | 16 | IEEE 754 binary16 | `Scalar[.float16]` | | `Float32` | 32 | IEEE 754 binary32 | `Scalar[.float32]` | | `Float64` | 64 | IEEE 754 binary64 | `Scalar[.float64]` | | `BFloat16` | 16 | Brain float | `Scalar[.bfloat16]` | | `Float4_e2m1fn` | 4 | OCP MX | `Scalar[.float4_e2m1fn]` | | `Float8_e4m3fn` | 8 | OFP8 | `Scalar[.float8_e4m3fn]` | | `Float8_e4m3fnuz` | 8 | -- | `Scalar[.float8_e4m3fnuz]` | | `Float8_e5m2` | 8 | OFP8 | `Scalar[.float8_e5m2]` | | `Float8_e5m2fnuz` | 8 | -- | `Scalar[.float8_e5m2fnuz]` | | `Float8_e8m0fnu` | 8 | OFP8 §5.4 | `Scalar[.float8_e8m0fnu]` | | `FloatLiteral` | arbitrary | -- | Compile-time only. Materializes to `Float64`. | :::note - IEEE-754 is the IEEE Standard for Floating-Point Arithmetic. - OFP8 is an 8-bit Floating Point Specification, which creates a standard for representing floating-point numbers in a compact format. ::: ### `Float16` {#float16} 16-bit IEEE 754 half-precision. The motivation is throughput and memory bandwidth: half the storage of `Float32` means twice the values fit in registers and cache, and GPU tensor cores process it at higher throughput. 1 sign bit, 5 exponent bits, 10 mantissa bits. The narrower exponent range limits dynamic range to roughly ±65504. Values beyond that overflow to infinity; very small values underflow to zero. This makes `Float16` workable for inference but less ideal for training, where gradients can span many orders of magnitude. Use `BFloat16` for training instead. `Float16` is natively accelerated on GPUs. On CPU, it requires ARM FP16 extension or Intel AVX-512 FP16. Other CPUs fall back to software emulation. ### `Float32` {#float32} 32-bit IEEE 754 single-precision. 23 mantissa bits give roughly 7 significant decimal digits; 8 exponent bits cover a range from roughly 1e-38 to 3.4e38. 1 sign bit, 8 exponent bits, 23 mantissa bits. `Float32` is natively accelerated on all GPU and CPU architectures. Use for general numeric work and GPU computation. ### `Float64` {#float64} 64-bit IEEE 754 double-precision. Use when 7 significant decimal digits aren't enough: scientific simulations, financial calculations, or accumulated sums where rounding errors compound. 52 mantissa bits give roughly 15-16 significant decimal digits. 1 sign bit, 11 exponent bits, 52 mantissa bits. ### `BFloat16` {#bfloat16} 16-bit brain floating-point developed by Google Brain for deep learning. 1 sign bit, 8 exponent bits, 7 mantissa bits. Google Brain designed it to solve a specific problem with `Float16` in training: `Float16`'s 5 exponent bits create a dynamic range too narrow for neural networks. Gradients overflow and underflow. `BFloat16` matches `Float32`'s 8 exponent bits exactly, so values stay in range throughout forward and backward passes. The matching exponent range also makes `Float32`/`BFloat16` conversion cheap: just truncate or extend the mantissa, no remapping. This makes mixed-precision training feasible: compute in `BFloat16` for speed and memory savings, keep optimizer state in `Float32` for precision. That combination drove its wide adoption as a training format. Use it for ML training and inference on supported hardware. The 7-bit mantissa is too imprecise for scientific or financial work. `BFloat16` is not supported on all platforms. It's currently unavailable on Apple Silicon. Natively accelerated on NVIDIA Ampere (A100) and later, AMD MI300X and later, and Intel CPUs with AMX or AVX-512 BF16 (Sapphire Rapids and later). ### Low-precision types Fewer bits per value means more values per register, less memory bandwidth, and higher throughput on specialized hardware. You trade mantissa precision for the ability to fit larger models or larger batches on the same silicon. These formats follow the OCP Microscaling Formats (MX) and OFP8 specifications. There's no single `Float8` type in Mojo. It's a colloquial umbrella for the five 8-bit floating-point variants exposed as `Scalar` aliases: `Float8_e4m3fn`, `Float8_e4m3fnuz`, `Float8_e5m2`, `Float8_e5m2fnuz`, and `Float8_e8m0fnu`. Each has its own exponent/mantissa layout and set of supported operations. `.float8_e3m4` also exists as a dtype value but has no `Scalar` alias; use `Scalar[.float8_e3m4]` directly. `Float8` formats are used in machine learning workloads where memory bandwidth matters more than precision. These types require GPU hardware for efficient execution. `Float8` types can't convert to or from any integer type on any platform, including `Bool`. They only convert between floating-point types: `Float16`, `Float32`, `Float64`, `BFloat16`, and other supported `Float8` variants. ### Floating point naming conventions The suffixes encode special properties of each format: - **`fn`**: finite -- no infinity or negative infinity encodings - **`uz`**: unsigned zero -- no negative zero encoding - **`fnu`**: finite, no sign, unsigned zero The name encodes the layout: `e4m3` means 4 exponent bits and 3 mantissa bits. `fn` means no infinities, and `uz` means unsigned zero. For example, `Float4_e2m1fn` is a 4-bit format with 2 exponent bits and 1 mantissa bit, defined by the Open Compute MX specification. :::note Vendor naming `Float8_e4m3fn` is the same format across vendors, but named differently: Mojo, PyTorch, JAX, and LLVM call it `e4m3fn`, while OCP, NVIDIA CUDA, and AMD ROCm call it `e4m3`. ::: ### Hardware requirements Support varies significantly by type and operation. None of these types support arithmetic at runtime on CPU. **Arithmetic support** (tested on ARM CPU, NVPTX sm_90a, AMDGCN gfx942): | Type | Comptime | CPU | NVPTX | AMDGCN | |-------------------|----------|-----|-------|--------| | `Float8_e4m3fn` | ✅ | ❌ | ✅ | ❌ | | `Float8_e4m3fnuz` | ✅ | ❌ | ❌ | ❌ | | `Float8_e5m2` | ✅ | ❌ | ✅ | ❌ | | `Float8_e5m2fnuz` | ✅ | ❌ | ❌ | ❌ | | `.float8_e3m4` | ❌ | ❌ | ❌ | ❌ | NVPTX support for `Float8_e4m3fn` and `Float8_e5m2` is emulated by the compiler: operands are upconverted to a wider type, the operation runs in that wider type, and the result is downconverted back. There are no native fp8 arithmetic instructions. - `Float8_e3m4` has no arithmetic support at any stage, including comptime. Most of its conversions work only at comptime. - `Float4_e2m1fn` requires NVIDIA Blackwell (B200) or later. - `Float32` and `Float64` are the portable alternatives for CPU and cross-platform code. ### IEEE 754 special values IEEE 754 floating-point types support special values: | Value | Meaning | |--------|-------------------| | `inf` | Positive infinity | | `-inf` | Negative infinity | | `nan` | Not a number | | `-0.0` | Negative zero | Access these via `SIMD` constants: ```mojo var x = Float32.MAX # largest value var y = Float32.MIN # smallest value var z = Float32.MAX_FINITE # largest finite value var w = Float32.MIN_FINITE # smallest (most negative) finite value ``` `MAX` and `MIN` may be infinite for floating-point types. `MAX_FINITE` and `MIN_FINITE` give the largest and smallest representable finite values. Low-precision formats marked `fn` (finite) don't have infinity encodings. Formats marked `uz` (unsigned zero) don't have negative zero. ### Floating point precision Floating-point arithmetic introduces rounding errors. Two values that look equal after computation may differ by a tiny amount. Comparing with `==` can give unexpected results: ```mojo # Compile-time: exact result comptime exact = 3.0 * (4.0 / 3.0 - 1.0) # Force runtime: rounding error appears var three = 3.0 var finite = three * (4.0 / three - 1.0) print(exact, finite) # 1.0 0.99999999999999978 print(exact == finite) # False ``` For approximate comparisons, check whether the difference is within an acceptable tolerance with `std.math`'s `isclose()`. ## Numeric literals Mojo has two compile-time literal types: `IntLiteral` and `FloatLiteral`. They support arbitrary precision and exist only during compilation. ### IntLiteral When you write a bare integer like `42`, its type is `IntLiteral`. It doesn't become a concrete type until it's used in a context that requires one: ```mojo var a: Int = 42 # Becomes Int var b: Int8 = 42 # Becomes Int8 var c: Float32 = 42 # Becomes Float32 var d: UInt64 = 1_000_000 # Becomes UInt64 ``` `IntLiteral` is arbitrary-precision at compile time. It has no fixed bit width, so compile-time calculations won't overflow or lose precision. At runtime, `IntLiteral` values materialize to `Int`: ```mojo # Compile-time: arbitrary precision, no overflow comptime big = 2 ** 200 # Runtime: materializes to Int (word-sized) var x = 42 # IntLiteral 42 materializes to Int ``` `IntLiteral` supports all arithmetic and comparison operators at compile time. ### FloatLiteral When you write a decimal constant like `3.14`, its type is `FloatLiteral`. It doesn't become a concrete type until it's used in a context that requires one: ```mojo var x: Float32 = 3.14 # Becomes Float32 var y: Float64 = 3.14 # Becomes Float64 var z: BFloat16 = 0.5 # Becomes BFloat16 ``` `FloatLiteral` provides compile-time constants for special values: | Constant | Value | |----------------------------------|-------------------| | `FloatLiteral.nan` | Not a number | | `FloatLiteral.infinity` | Positive infinity | | `FloatLiteral.negative_infinity` | Negative infinity | | `FloatLiteral.negative_zero` | Negative zero | Use `is_nan()` and `is_neg_zero()` to test for these values, since `nan == nan` is `False` and `negative_zero == 0.0` is `True`. ### Literals in expressions Literals adapt to the types around them. When a literal appears next to a typed value, it takes on that value's type: ```mojo var x = Float32(1.0) var y = x * 0.5 # 0.5 becomes Float32 var z = x + 2 # 2 becomes Float32 ``` This isn't implicit conversion. The literal doesn't have a runtime type yet. It becomes whatever type the context requires. Variables have a fixed type and never convert implicitly. ## Explicit conversions Converting between numeric types always requires an explicit initializer or cast. Mojo doesn't perform implicit numeric conversions between variables: ```mojo var i = 42 # Int var f = Float32(i) # Int → Float32 var u = UInt64(i) # Int → UInt64 var narrow = Int8(i) # Int → Int8 ``` Between `SIMD`-based types, use `.cast[]`: ```mojo var a = Float32(3.14) var b = a.cast[.int32]() # Float32 → Int32 var c = a.cast[.float64]() # Float32 → Float64 ``` Between `Int` and `SIMD`-based types, use initializers: ```mojo var i = 42 # Int var s = Int64(i) # Int → Int64 var back = Int(s) # Int64 → Int ``` ### Why conversions are explicit Implicit numeric conversions can hide precision loss and sign changes. For example, `Int64(-1)` becoming `UInt64(18446744073709551615)` is a bug, not a convenience. Mojo requires an explicit conversion so the intent is clear. Literals are the exception. A literal like `42` can become `Float32(42.0)` because the compiler performs the conversion at compile time and can guarantee it is exact. Variables are different. A value like `x: Int = 300` becoming an `Int8` would silently lose data, so Mojo requires you to write the conversion explicitly. ## Sharp edges ### `Int` width is platform-dependent `Int` is 64-bit on most platforms today, but it's defined as machine width. Code that assumes 64-bit `Int` will break on 32-bit targets. Use `Int64` when you need a fixed width. ### Integer arithmetic wraps on overflow Integer arithmetic wraps on overflow using two's complement: - Signed overflow wraps into the negative range. Adding `1` to `Int8` value `127` produces `-128`. - Unsigned overflow wraps to zero. Adding `1` to `UInt8` value `255` produces `0`. Mojo doesn't trap on overflow. If you need overflow detection, check the operands before the operation. ```mojo var x = Int8(127) var y = x + Int8(1) # -128 (wraps) ``` ### Float-to-int truncates toward zero ```mojo var x = Int(Float32(3.9)) # 3, not 4 var y = Int(Float32(-3.9)) # -3, not -4 ``` ### NaN comparisons always return `False` This includes `NaN == NaN`. It affects SIMD masks and conditional selection: ```mojo var x = Float32.MAX * 2.0 # inf var nan = x - x # NaN print(nan == nan) # False ``` ### 128-bit and 256-bit integers are software-emulated `Int128`, `Int256`, `UInt128`, and `UInt256` exist but have limited hardware support on most platforms. Avoid them in performance-critical code without benchmarking. ### Float8 types require GPU hardware The `Float8` variants are designed for ML workloads on GPUs with native support. On CPUs, operations on these types may be emulated or unavailable. --- ## Mojo operator reference ## Precedence table Operator precedence and associativity. Higher-precedence operators bind tighter. Unless noted, operators associate left to right. ### From highest to lowest precedence | Precedence | Operators | Notes | |------------|-----------------------------|-----------------------------------| | 1 | `()` `[]` `.` | Call, subscript, attribute | | 2 | `**` | Exponentiation, right-associative | | 3 | `+x` `-x` `~x` | Unary prefix | | 4 | `*` `@` `/` `//` `%` | Multiplicative | | 5 | `+` `-` | Additive (addition, subtraction) | | 6 | `<<` `>>` | Bitwise shift (left, right) | | 7 | `&` | Bitwise AND | | 8 | `^` | Bitwise XOR (not transfers) | | 9 | `\|` | Bitwise OR | | 10 | `==` `!=` `<` `<=` `>` `>=` | Comparisons, chainable | | 10 | `in` `not in` | Membership, chainable | | 10 | `is` `is not` | Identity, chainable | | 11 | `not` | Boolean NOT, prefix | | 12 | `and` | Boolean AND, short-circuits | | 13 | `or` | Boolean OR, short-circuits | | 14 | `if`-`else` | Ternary, right-associative | | 15 | `:=` | Walrus operator | _Prefix operators_: positive (`+x`), negative (`-x`), bitwise NOT complement (`~x`) _Multiplicative operators_: times (`*`), matrix multiplication (`@`), divide (`/`, integer types round towards zero), flooring divide (`//`, integer types round towards negative infinity), modulo (`%`). _Comparison operators_: equality (`==`), inequality (`!=`), less-than (`<`), less-than-or-equal (`<=`), greater-than (`>`), greater-than-or-equal (`>=`) :::note Assignment operators (`=` `+=` `-=` `*=` `/=` `//=` `%=` `**=` `@=` `&=` `\|=` `^=` `<<=` `>>=`) are statements, not expressions. They are not part of expression precedence. ::: ## Right-associative operators Most operators are left-associative. For example, `a - b + c` groups as `(a - b) + c`. Two infix operators are right-associative: exponentiation (`a ** b`) and Mojo's ternary `if`-`else`. ### Exponentiation (`**`) ```mojo 2 ** 3 ** 4 ``` groups as ```mojo 2 ** (3 ** 4) ``` Equivalent to `pow(2, 81)`. In Mojo `pow(a, b)` and `a ** b` are interchangeable. ### Ternary (`if`-`else`) ```mojo "low" if value < 10 else "high" if value > 100 else "mid" ``` groups as ```mojo "low" if value < 10 else ("high" if value > 100 else "mid") ``` ## Chaining operations All comparison operators can be chained: ```mojo a < b < c # equivalent to: (a < b) and (b < c) a == b == c # equivalent to: (a == b) and (b == c) a < b == c # equivalent to: (a < b) and (b == c) a < b <= c != d # equivalent to: (a < b) and (b <= c) and (c != d) ``` Each intermediate value is evaluated once. - Chaining only applies between operators at the same precedence. `2 ** 3 == 8` isn't a chain. It evaluates as `(2 ** 3) == 8` since exponentiation binds tighter than comparison. - Comparison, membership, and identity operators share the same precedence and chain together. `5 != a < b in c` is valid and evaluates as `(5 != a) and (a < b) and (b in c)`. ## Implementing operators for custom types Mojo doesn't limit operators use to built-in types. Each operator has a set of dunder methods your custom types can implement. Once added, you can use operators in code instead of calling methods. ### Infix operator method types Each infix operator has up to three forms that decide which operand's method will run. For `a op b`: - _Forward_: Mojo tries the forward method first. `a + b` calls `a.__add__(b)`. - _Reversed_: If the forward method doesn't exist or can't handle `b`'s type, Mojo falls back to the reversed method on `b`. `a + b` calls `b.__radd__(a)`. - _In-place_: Called for compound assignment. `a += b` calls `a.__iadd__(b)`. For example, if `a` uses a `CustomVector` type: - `a + 5`: calls the forward method `a.__add__(5)` - `5 + a`: Int doesn't know custom types. Falls back to the reversed method, `a.__radd__(5)` - `a += 5`: calls the in-place `a.__iadd__(5)` method ### Arithmetic Implement the methods directly on your struct to use `instance OP instance`, `instance OP= instance`. | Operator | Forward | Reversed | In-place | |----------|----------------|-------------------|-------------------| | `+` | `__add__()` | `__radd__()` | `__iadd__()` | | `-` | `__sub__()` | `__rsub__()` | `__isub__()` | | `*` | `__mul__()` | `__rmul__()` | `__imul__()` | | `/` | `__truediv__` | `__rtruediv__()` | `__itruediv__()` | | `//` | `__floordiv__` | `__rfloordiv__()` | `__ifloordiv__()` | | `%` | `__mod__()` | `__rmod__()` | `__imod__()` | | `**` | `__pow__()` | `__rpow__()` | `__ipow__()` | | `@` | `__matmul__()` | `__rmatmul__()` | `__imatmul__()` | In-place operators are syntactic sugar for the operator applied to the variable with assignment: ```mojo x += y # x = x + y x -= y # x = x - y x *= y # x = x * y x /= y # x = x / y x //= y # x = x // y x %= y # x = x % y x **= y # x = x ** y x @= y # x = x @ y ``` **Traits:** [`Powable`](/docs/std/math/math/Powable/) requires `__pow__()`, doesn't provide a default. ### Bitwise Implement the methods directly on your struct to use `a OP b`, `a OP= b`. | Operator | Forward | Reversed | In-place | |----------|----------------|-----------------|-----------------| | `&` | `__and__()` | `__rand__()` | `__iand__()` | | `\|` | `__or__()` | `__ror__()` | `__ior__()` | | `^` | `__xor__()` | `__rxor__()` | `__ixor__()` | | `<<` | `__lshift__()` | `__rlshift__()` | `__ilshift__()` | | `>>` | `__rshift__()` | `__rrshift__()` | `__irshift__()` | Bitwise operators are typically implemented on integer and flag types. Like arithmetic operators, in-place bitwise operators are syntactic sugar for the operator applied to the variable with assignment: ```mojo x &= y # x = x & y x |= y # x = x | y x ^= y # x = x ^ y x <<= y # x = x << y x >>= y # x = x >> y ``` ### Unary operators Implement the methods directly on your struct to support prefix operators like `-x`, `+x`, and `~x`. | Operator | Method | |----------|----------------| | `-x` | `__neg__()` | | `+x` | `__pos__()` | | `~x` | `__invert__()` | Mojo offers one postfix unary operator. | Operator | Method | |----------|------------------------------| | `x^` | Compiler implementation only | Use the `^` sigil for ownership transfer. `consume(a^)` transfers ownership of `a` to the `consume` function. - If the `consume` argument uses the `var` convention, the transfer moves the value. - If not, the value is copied and the original is left intact. After transfer, the `a` variable is uninitialized. You can't re-use the name until it's assigned a new value. ```mojo consume(a^) # transfers ownership of `a`'s value, leaving `a` uninitialized ``` **Disambiguation:** `^` means XOR when followed by a value, and transfer when used directly after a variable name. ```mojo a ^ b # XOR a^ # transfer ``` ### Comparison operators Implement the methods directly on your struct to use `a OP b`. | Operator | Method | Trait | Default? | |----------|------------|--------------|----------| | `==` | `__eq__()` | `Equatable` | Yes | | `!=` | `__ne__()` | `Equatable` | Yes | | `<` | `__lt__()` | `Comparable` | No | | `<=` | `__le__()` | `Comparable` | Yes | | `>` | `__gt__()` | `Comparable` | Yes | | `>=` | `__ge__()` | `Comparable` | Yes | **Traits:** [`Equatable`](/docs/std/builtin/comparable/Equatable/) provides `__eq__()` if all your struct's fields conform to `Equatable` using pairwise field comparison. `__ne__()` derives from `__eq__()`. [`Comparable`](/docs/std/builtin/comparable/Comparable/) provides `__le__()`, `__gt__()`, and `__ge__()`, all derived from `__lt__()`. You implement `__lt__()`. `Comparable` refines `Equatable`, so conforming to `Comparable` requires both traits. If all your fields are `Equatable`, you implement `__lt__()` at a minimum. ### Identity and membership operators Implement the methods directly on your struct to use `a OP b`. | Operator | Method | Trait | Default? | |----------|------------------|----------------|----------| | `is` | `__is__()` | `Identifiable` | No | | `is not` | `__isnot__()` | `Identifiable` | Yes | | `in` | `__contains__()` | — | No | | `not in` | `__contains__()` | — | No | `x in collection` calls `collection.__contains__(x)`. The method is on the **container**, not the element. `not in` calls the same method and negates the result. `is` tests object identity, not equality. Stdlib types that implement it include `ArcPointer`, `PythonObject`, and `Optional` (for `is None` checks). **Traits:** [`Identifiable`](/docs/std/builtin/identifiable/Identifiable/) requires `__is__()`. `__isnot__()` is provided (calls `not (self is rhs)`). ### Subscript operators Implement the methods directly on your struct to use `a[key]` reads and `a[key] = b` assigns. | Operation | Method | |--------------------------|-----------------| | `obj[key]` (read) | `__getitem__()` | | `obj[key] = val` (write) | `__setitem__()` | Both accept variadic arguments (for multi-dimensional indexing). --- ## Mojo simple statements reference A *simple statement* performs a single action on one logical line. Multiple simple statements can share a line when separated by semicolons. ## Import statements *Import statements* expose modules and their members to the current scope. Imports can appear at module level, inside functions, or inside other scopes. They don't need to appear at the top of a file. ```mojo import std.math from std.collections import Dict, Set ``` Use parentheses to import on multiple lines for readability and support clean commit diffs: ```mojo from std.collections import ( Dict, Set, List, # Trailing comma is legal ) ``` ### Module imports ```mojo import std.math import numpy as np # Alias the module name to avoid collisions ``` ### Selective imports ```mojo from std.math import sqrt, pi from std.collections import Dict as Dictionary # Alias the imported name ``` ### Wildcard imports ```mojo from std.math import * # Imports all public names from the std.math module ``` ## Expression statements An *expression statement* evaluates an expression for its side effects. When the result is unused (other than `None`), the compiler warns: ```mojo x + y # Warning: result is unused ``` The compiler doesn't warn when the result is `None`, which is common for functions called for their side effects: ```mojo print("hello") # Side effect: prints trigger() # Side effect: called for behavior ``` Assign the result to `_` to explicitly discard it and silence the warning: ```mojo _ = update() # Explicitly discard the result ``` Expressions are not valid at module scope or in struct bodies outside of methods. ## Assignment statements *Assignment statements* bind values to names with `=`. Use `var` declarations to declare new variables: ```mojo var x = 42 var name = "Alice" var result = compute() ``` Use `ref` declarations to create reference bindings: ```mojo ref y = my_list[3] # `y` is a reference to the value at `my_list[3]` ``` The `y` reference binding does not create a new value. It creates a reference to the existing value at `my_list[3]`. Modifying `y` modifies the value in `my_list[3]`; modifying `my_list[3]` modifies what `y` reads. Annotated assignments bind a type to a name, with an optional initializer. Type annotations aren't required, but they improve readability and catch errors: ```mojo var x: Int = 42 var name: String = "Alice" var values: List[Float64] = [] ``` A `var` without a type *and* without an initializer is an error: ```mojo var x # Error: declaration must have either a type or an initializer var x: Int # OK: type provided, value uninitialized var x = 42 # OK: type inferred from initializer ``` When types are complex and long to write, you can use comptime aliases to keep your code concise: ```mojo comptime Vec3 = List[Float64] var position: Vec3 = [0.0, 0.0, 0.0] ``` ### Multiple assignment Multiple assignments give you a concise syntax for initializing variables. Assign the same value to multiple names. This is right-associative. `z` is assigned first, then `y`, then `x`. If the RHS has side effects, they run once: ```mojo # Good for initializing counters/flags to the same literal. var x = var y = var z = "Hello" print(x, y, z) # Hello Hello Hello # Mixing conventions ref a = var b = var c = "Hello" print(a, b, c) a = "World" print(b) # World ``` Destructuring assignment: ```mojo var a, b = 1, 2 # Destructuring assignment var (c, d) = (1, 2) # Equivalent destructuring # not "assign tuple to tuple" print(a, b, c, d) # 1 2 1 2 def returns_pair() -> Tuple[Int, Int]: return (1, 2) var e, f = returns_pair() print(e, f) # 1 2 ``` Avoid multiple assignments for destructuring unrelated values. ```mojo var temperature, name = 98.6, "Bob" ``` reads worse than two lines: ```mojo var temperature = 98.6 var name = "Bob" ``` ### Simple swaps ```mojo var a, b, c = 1, 2, 3 a, b, c = c, a, b print(a, b, c) # 3 1 2 ``` ### Augmented assignment Augmented assignment is syntactic sugar that combines an operation with assignment. The left-hand side is evaluated once: ```mojo x += 5 # x = x + 5 x -= 2 # x = x - 2 x *= 3 # x = x * 3 x /= 4 # x = x / 4 x //= 2 # x = x // 2 x %= 7 # x = x % 7 x **= 2 # x = x ** 2 x @= m # x = x @ m (matrix multiply) x &= mask # x = x & mask x |= flags # x = x | flags x ^= bits # x = x ^ bits x <<= 1 # x = x << 1 x >>= 1 # x = x >> 1 ``` ## The pass statement `pass` is a no-op. Use it as a placeholder where a statement is required but no action is needed: ```mojo def not_ready(): pass struct Empty: pass ``` `pass` is required in empty function and struct bodies to avoid syntax errors. ## The return statement `return` exits a function and optionally returns a value: ```mojo def greet(name: String): if not name: # String is falsy when empty return print(t"Hello, {name}!") def get_value() -> Int: return 42 def early_exit(items: List[Int], target: Int) -> Bool: for item in items: if item == target: return True return False ``` A function without an explicit `return` implicitly returns `None`. `return` is only valid inside a function: ```mojo return 42 # Error: cannot return from this context ``` ## The raise statement `raise` raises an error. The function must be declared with `raises` or included within a `try` block: ```mojo def validate(value: Int) raises -> Bool: if value < 0: raise Error("value must be non-negative") return True def mitigate_risk(): try: if not perform_some_test(): raise Error("test failed") # perform risky work, knowing test passed except e: log(e) ``` To propagate errors from a `try` block, use a bare `raise` to re-raise the current error: ```mojo try: validate(value) # perform work, knowing value is valid except e: raise # Re-raises current error to the next handler ``` Raising outside a valid context is an error: ```mojo raise Error("oops") # Error: cannot raise error in this context # (surround with try, or mark function as raises) raise # Error: no contextual error to reraise # (bare raise requires an active except block) ``` ## Control flow with break and continue statements `break` exits the innermost loop immediately. `continue` skips to the next iteration: ```mojo for x in range(10): if x == 5: break # Stop at 5 if x % 2 == 0: continue # Skip even numbers print(x) # Prints 1, 3 ``` ## Compile-time declarations `comptime` declares a compile-time constant. The value must be computable at compile time: ```mojo comptime SIZE = 256 comptime MAX = SIZE * 2 ``` `comptime` declares associated types in traits, and can be used to create type aliases and trait composition aliases: ```mojo comptime Permissive = ImplicitlyCopyable & Deinitable trait SimpleTrait(Writable): # `SimpleTrait` refines `Writable` comptime Element = Permissive # Associated type with a comptime alias ``` A trailing `where` clause constrains a parametric declaration's parameters. The compiler checks the condition at each use: ```mojo comptime AscendingMidpoint[lo: Int, hi: Int]: Int where lo < hi = (lo + hi) / 2 def main(): comptime mid = AscendingMidpoint[2, 10] # 6 # comptime bad = AscendingMidpoint[10, 2] # Error: lo < hi not satisfied ``` A `where` clause requires conditions the compiler can evaluate at compile time, such as comparisons, boolean combinations, and `conforms_to()`. --- ## Mojo struct declarations reference A struct defines a custom type with fields and methods. Structs are value types: each variable holds its own independent copy rather than a reference to shared data. ```text struct Name: body struct Name[parameter-list]: body struct Name(TraitA, TraitB): body struct Name[parameter-list](TraitA, TraitB): body ``` By convention, struct names use `PascalCase`. `Self` (capital S) refers to the struct's own type inside the body. `self` (lowercase) is a conventional argument name for the instance. ```mojo from std.math import sqrt struct Point: var x: Int var y: Int def __init__(out self, x: Int, y: Int): self.x = x self.y = y def distance(self) -> Float64: return sqrt( Float64(self.x * self.x + self.y * self.y) ) def main(): var p = Point(3, 4) print(p.distance()) # 5.0 ``` ## Struct body elements A struct body can contain these elements: | Element | Syntax | Role | |-----------------------|-------------------------------|----------------------------| | Field | `var name: Type` | Instance data | | Method | `def name(self, ...)` | Instance behavior | | Static method | `@staticmethod def name(...)` | Type-level behavior | | Compile-time constant | `comptime name = value` | Evaluated at compile time | | Initializer | `def __init__(out self, ...)` | Constructs an instance | | Deinitializer | `def __deinit__(deinit self)` | Cleanup at end of lifetime | The most minimal struct uses `pass` for an empty body: ```mojo struct ValidationError: pass ``` Structs can't be nested inside other structs, traits, or functions: ```mojo struct Outer: struct Inner: # Error: nested struct not supported here pass ``` ## Fields Declare each field with `var` and a type annotation. Fields can't have default values. All fields must be initialized in `__init__()`: ```mojo struct Color: var r: UInt8 var g: UInt8 var b: UInt8 def __init__(out self, r: UInt8, g: UInt8, b: UInt8): (self.r, self.g, self.b) = (r, g, b) ``` Every field requires a type annotation: ```mojo struct Unsound: var x # Error: struct field declaration must have a type ``` Field types must be concrete, not traits. A struct parameter establishes a concrete type at compile time: ```mojo struct Unsound: var item: Writable # Error because dynamic traits not supported @fieldwise_init struct Sound[T: Writable & Copyable & Deinitable]: var item: Self.T # OK: concrete at compile time def main(): var g = Sound[Int](item=42) print(g.item) # 42 ``` ### Synthesized initializers The `@fieldwise_init` decorator synthesizes an `__init__()` from the struct's fields: ```mojo @fieldwise_init struct Color: var r: UInt8 var g: UInt8 var b: UInt8 def main(): var color = Color(255, 0, 0) print(color.r, color.g, color.b) # 255, 0, 0 ``` Synthesis fails if any field is non-copyable and non-movable: ```mojo @fieldwise_init struct Alpha: var a: UInt8 @fieldwise_init struct Color: var r: UInt8 var g: UInt8 var b: UInt8 var alpha: Alpha # Error: cannot synthesize fieldwise init because field # 'alpha' has non-copyable and non-movable type 'Alpha' ``` ### Recursive references Structs can't point to themselves. Mojo won't let you build a type that stores another instance of itself, even when nested within an Optional: ```mojo struct Node: var value: String var next: Optional[Node] # Error about this being a recursive # reference ``` To build recursive data structures such as linked lists and trees, you must use unsafe pointers. ## Parameters Structs accept compile-time parameters in square brackets. Parameters are accessed through `Self` inside the struct body. `Self.T` refers to the parameter `T`. Bare `T` isn't valid in the struct body: ```mojo @fieldwise_init struct Pair[T: Copyable & Deinitable]: var first: Self.T var second: Self.T ``` ## Trait conformance Declare conformance in parentheses after the name or parameter list. Separate multiple traits with commas or compose them with `&`: ```mojo @fieldwise_init struct MyInt(Writable, Copyable): var value: Int def write_to[W: Writer](self, mut writer: W): writer.write(self.value) def main(): var my_int = MyInt(42) print(my_int) # 42 ``` Conformance commits the struct to implementing every method and associated type the trait requires. Missing items produce errors: ```mojo @fieldwise_init struct Incomplete(Sized): var value: Int # Error: 'Incomplete' does not implement all requirements # for 'Sized' # Note: required function '__len__' is not implemented ``` ### Conformance lists A conformance list accepts traits and conditional `where` clauses: ```mojo @fieldwise_init struct Pair[T: Copyable & Deinitable]( Equatable where conforms_to(T, Equatable) ): var first: Self.T var second: Self.T ``` ### Implicit conformances The compiler automatically conforms every struct to `AnyType` and `Deinitable` when all members are also `Deinitable`. With parameterized types, the parameter's traits must include `Deinitable` for this to apply: ```mojo @fieldwise_init struct Box[T: Copyable & Deinitable]( Equatable where conforms_to(T, Equatable) ): var item: Self.T def main(): var box = Box(42) print(box.item) # OK ``` Without `Deinitable`, the compiler can't verify that the struct is safe to destroy using the built-in `__deinit__()` deinitializer: ```mojo @fieldwise_init struct Box[T: Copyable]( Equatable where conforms_to(T, Equatable) ): var item: Self.T def main(): var box = Box(42) print(box.item) # Error about the 'box' being abandoned without being destroyed. ``` ### Synthesized lifecycle methods If a struct conforms to `Movable` but doesn't define `__init__(move:)`, the compiler synthesizes one that moves each field. The same applies to `Copyable` and `__init__(copy:)`. Synthesis fails if any field can't support the operation: ```mojo struct Unsound(Copyable): var item: SomeMoveOnlyType # Error about synthesizing the copy initializer because # field 'item' has non-copyable type SomeMoveOnlyType ``` ### Default method conflicts When two traits provide conflicting defaults for the same method, the struct must implement it manually: ```mojo trait A: def foo(self) -> Int: return 42 trait B: def foo(self) -> Int: return 1024 @fieldwise_init struct S(A & B): pass # Error about conflicting default implementations in two traits # reminding you to implement the implementation manually ``` ### Conditional conformance Conditional conformance lets a struct conform to a trait when certain conditions are met. For example, the following structs conform to a set of (mostly hypothetical) traits when their parameters meet specific criteria: ```mojo from std.sys import is_gpu @fieldwise_init struct Mathematical( GPUComputable where is_gpu() ): # conforms only on GPU targets @fieldwise_init struct FixedBuffer[T: Copyable, N: Int]( Iterable where N > 0 ): # conforms if N is one or more, but not if N is zero or negative @fieldwise_init struct Tensor[dtype: DType]( FloatMath where dtype.is_floating_point() ): # conforms when dtype is a floating point type @fieldwise_init struct Tagged[kind: StringLiteral]( Printable where kind == "debug" ): # only conforms in debug mode @fieldwise_init struct Box[T: Copyable]( Equatable where conforms_to(T, Equatable) ): # conforms to Equatable only when T does ``` ### Conditional conformance and compile-time values Conditional conformance can depend only on information known at compile time. While it often uses traits to constrain conformance, conditional conformance isn't limited to traits. A condition can use any compile-time value that can be evaluated in a clear and consistent way. For example, a type might conform to a trait only on a specific platform (such as NVIDIA GPUs or Apple Silicon with Metal) or when a compile-time constant has a given value. If the condition can be fully resolved at compile time, it can restrict conformance. That said, conformance can't depend on a computed compile-time member. Trait conformance is part of the type's signature, and the signature is needed to resolve members. Depending on a computed member would create a circular dependency. ### Conditional conformance and default implementations Conditional conformance and default implementations are independent features, but they often work together. The `Writable` trait offers a default implementation that uses reflection to automatically write struct fields. Declare the trait conformance after ensuring that all fields are `Writable`: ```mojo @fieldwise_init struct Point(Writable): var x: Float64 var y: Float64 ``` Consider a parameterized version of this `Pair` type: ```mojo @fieldwise_init struct Pair[T: Copyable & Deinitable]: var first: Self.T var second: Self.T ``` If `T` isn't `Writable`, the struct can still declare `Writable` conformance by providing its own implementation of `Writable`'s required methods. This is impractical without an API surface that describes `T` instances. A better solution is to use conditional conformance. Ensure `Pair` conforms to `Writable` only when its fields do. Test the `T` type with `conforms_to()` in a `where` clause in the struct's conformance list: ```mojo @fieldwise_init struct Pair[T: Copyable & Deinitable]( Writable where conforms_to(T, Writable) ): var first: Self.T var second: Self.T ``` You can print `Pair[Int]` because `Int` is `Writable`, but not `Pair[NotWritable]`, which doesn't conform to `Writable`: ```mojo @fieldwise_init struct NotWritable(ImplicitlyCopyable & Deinitable): var item: Int def main(): var not_writable = NotWritable(42) var pair = Pair(not_writable, not_writable) print(pair) # Error regarding 'Writable' nonconformance ``` ### Mixed trait lists You can combine conditional and non-conditional traits in the same conformance list: ```mojo @fieldwise_init struct Pair[T: Copyable & Deinitable]( Equatable where conforms_to(T, Equatable), Writable where conforms_to(T, Writable), Copyable ): var first: Self.T var second: Self.T def main(): var pair1 = Pair(first=1, second=2) var pair2 = Pair(first=1, second=2) var pair3 = Pair(first=3, second=4) print(pair1 == pair2) # True print(pair1 == pair3) # False var pair4 = pair1.copy() # OK: Copyable conformance doesn't depend on T _ = pair4 ``` ## Methods Instance methods take `self` as the first argument. The convention on `self` determines access: - Bare `self`: immutable reference. - `mut self`: allows modification. - `out`, `deinit`: used by lifecycle methods. - `out`: also used to specify a named result slot. - `ref`: used to declare an argument with parametric mutability, and which must be passed in memory regardless of its type. A method without `self` is an error unless it's marked `@staticmethod`: ```mojo struct Unsound: def broken(): pass # Error: self argument must be present in instance method struct OK: @staticmethod def utility(): # No self required pass ``` `@staticmethod` marks a method that belongs to the type, not to instances. It can access type parameters and comptime members, can call other static methods, has no `self`, and has no access to instance fields and instance methods. Use static methods for utility functions related to the struct's mission that don't need an instance to work, such as factory methods and general-purpose helpers. ### Dunder methods *Dunder methods* (double-underscored names) let a struct work with operators, built-in functions, and lifecycle events: `__init__()`, `__add__()`, `__str__()`, and so on. ## Instance creation with initializers `__init__()` uses the `out self` convention to produce the newly initialized value. Every field must be assigned before `__init__()` returns: ```mojo struct Point: var x: Float64 var y: Float64 def __init__(out self, x: Float64, y: Float64): (self.x, self.y) = (x, y) def main(): var p = Point(3.0, 4.0) print(p.x, p.y) # 3.0 4.0 ``` Omitting `out self` is an error: ```mojo struct Unsound: var value: Int def __init__(self): pass # Error: __init__ method must return Self type with # 'out' argument ``` `__init__()` is implicitly static. Structs can define multiple `__init__()` overloads. ## Instance tear-down with deinitializers `__deinit__()` runs when the compiler detects no further access to an instance. Its `self` uses the `deinit` convention: ```mojo def __deinit__(deinit self): print("cleaning up") ``` Implicitly deinitable structs get a default `__deinit__()`. A custom `__deinit__()` overrides the default. `__deinit__()` can't be overloaded. :::note Explicitly destroyed structs write their own cleanup logic, using a consuming method with `deinit self`. Opt out with `Deinitable where False`. ::: ## `comptime` members `comptime` declares type-level members inside a struct. They're evaluated at compile time and can't be modified at runtime. Use them for constants, type aliases, and computed members: ```mojo @fieldwise_init struct Matrix2D[dtype: DType, w: Int, h: Int]: pass struct Test[dtype: DType]: comptime default_size = 1024 comptime DefaultMatrixType = Matrix2D[Self.dtype, Self.default_size, Self.default_size] comptime SquareMatrixType[size: Int] = Matrix2D[Self.dtype, size, size] def main(): print(Test[.int32].default_size) # 1024 ``` Access these constants on the instance or the type. For example, `Test[.int32]().default_size` and `Test[.int32].default_size`. --- ## Mojo trait declarations A *trait* defines requirements that a conforming type must satisfy, including methods, associated types, and constants. Traits are similar to *protocols* in Swift, *interfaces* in Java, and *traits* in Rust. When a type conforms to a trait, the compiler checks every requirement and rejects the code if anything is missing. ## Trait declarations Traits are declared with the `trait` keyword followed by the trait name and an optional refinement list. The body contains the trait's requirements, both required and provided: ```text trait Name: body trait Name(ParentA, ParentB): body ``` Traits must be declared at the top level of a file. They can't be nested inside structs, other traits, or functions: ```mojo struct Outer: trait Inner: # Error: nested trait not supported here ... ``` ### Marker traits Empty traits are called *marker traits* and signal that a type has a specific property or capability without refining other traits or declaring requirements: ```mojo trait AnyType: pass ``` Mojo marker traits include: `AnyType`, `TrivialRegisterPassable`, `RegisterPassable`, and `ImplicitlyCopyable`. They tell the compiler about a type's properties, supporting compile-time optimizations. ## Trait body elements A trait body can contain these elements: | Element | Syntax | Role | |-----------------------------------|-------------------------|------------------------------------------| | Required method | `...` body | Conforming types must implement | | Provided method | Code body | Inherited unless overridden | | Comptime member - associated type | `comptime Name: Trait` | Conforming types provide a concrete type | | Comptime member - required value | `comptime name: Type` | Conforming types provide a value | | Comptime member - constant | `comptime name = value` | Shared across all conforming types | ## Trait and member names Trait names must be valid identifiers. By convention, they describe a capability: `Writable`, `Hashable`, `Copyable`. ### Naming conventions | Element | Naming | Notes | |--------------------|--------------------------------------------------------------------|--------------------------------------------------------------------------| | Trait name | `PascalCase` | Capabilities gained by conformance: `Equatable`, `Copyable`, `PathLike`. | | Instance method | `lower_snake_case()` | Method's action: `write_to()`, `update()` | | Static method | `lower_snake_case()` | Method's action: `get_type_name`, `get_element_bitwidth` | | `comptime` members | Trait compositions are `PascalCase`. Values are `lower_snake_case` | Describes use: `KeyElement`, `element_bitwidth` | | Associated type | `PascalCase` | Describes use: `Element`, `Iterator` | :::caution When defining traits, avoid private member names with single or double underscore prefixes. It may cause required members to be hidden in generated documentation from trait users. For similar reasons, don't use `@doc_hidden` to hide trait members. The standard library has a small number of exceptions for required trait elements known to the compiler. ::: ## Methods Traits define both required and provided methods, and both instance and static members. Instance methods take `self` as the first parameter. Static methods require the `@staticmethod` decorator. ### Required methods An ellipsis (`...`) marks a required method. Conforming types must provide an implementation: ```mojo trait RequiredMethods: def required_method(self): ... @staticmethod def required_static_method(): ... @fieldwise_init struct SampleStruct(RequiredMethods): def required_method(self): print("Required method") @staticmethod def required_static_method(): print("Required static method") def main(): var s = SampleStruct() s.required_method() # Required method SampleStruct.required_static_method() # Required static method ``` ### Provided methods A method with a body other than `...` provides a default implementation. Conforming types automatically receive that behavior but can also override it: ```mojo trait ProvidedMethods: def provided_method(self): print("Provided method") @staticmethod def provided_static_method(): print("Provided static method") @fieldwise_init struct SampleStruct(ProvidedMethods): def provided_method(self): print("Overridden provided method") def main(): var sample = SampleStruct() sample.provided_method() # Overridden provided method SampleStruct.provided_static_method() # Provided static method ``` Both required and provided methods can return values: ```mojo trait Describable: def provided_describe(self) -> String: return "no description" # Type can override this implementation def required_describe(self) -> String: ... # Type must provide an implementation for this method ``` Provided behavior can't use implementation details from any specific conforming type, as a trait has no knowledge of a type's capabilities beyond those declared in the trait and refinement list. The behavior must work across all conforming types. ### `pass` vs `...` `pass` and `...` mean distinct things in trait bodies: - `...` marks a required method stub. - `pass` is a no-op that counts as a provided implementation body. It's only valid when the method returns `None`. If a method declares a return type but uses `pass` as its body, the compiler will encourage you to replace it with `...`: ```mojo trait Unsupported: def __compute__(self) -> Int: pass # Error because trait method with a return type must not use 'pass'. # Use '...' to declare the method as required. ``` ## Comptime members: associated types An associated type is a `comptime` member that declares a related subordinate type that conforming types must specify. For example, an associated type might be the `Element` type for a container or collection trait, or the `Key` and `Value` types for a map. The associated type is declared as a `comptime` member without an initializer. Only traits can use this declaration form. Conforming structs provide the concrete value that satisfies any constraints declared in the trait. In the following example, `Self` refers to the conforming type, so `Self.Associated` refers to the conforming type's value for the associated type `Associated`: ```mojo trait Boxable: comptime Associated: Writable & Copyable & Deinitable def unbox(self) -> Self.Associated: ... @fieldwise_init struct ConcreteBox(Boxable): comptime Associated = String var value: Self.Associated def unbox(self) -> Self.Associated: return self.value.copy() def main(): var box = ConcreteBox(value="Hello") var unboxed = box.unbox() # Known to be Copyable print(unboxed) # Known to be Writable _ = unboxed^ # Known to be Deinitable ``` Associated types can be assigned from call sites. This lets a trait work across type families: ```mojo comptime Base = Copyable & Deinitable & Writable @fieldwise_init struct Box[T: Base](Boxable): comptime Associated = Self.T var value: Self.Associated def unbox(self) -> Self.Associated: return self.value.copy() ``` A `comptime` without an assignment, type, or traits is an error: ```mojo trait Unsupported: comptime X # Error: expected '=' after comptime declaration trait Supported: comptime X: Copyable # OK: associated type comptime y: Int # OK: required value comptime z = 42 # OK: constant ``` Outside of traits, a `comptime` member without an initializer is an error: ```mojo struct Unsupported: comptime X: Int comptime Y: Copyable # Error: only traits may contain a comptime member # without an initializer ``` ## Comptime members: constants and required values A trait can declare `comptime` constants (shared value) and required assignments (conforming types must provide a value). ### Constants This trait provides a usable bitwidth for conforming types based on the `Element` associated type and a named trait composition: ```mojo from std.sys.info import bit_width_of comptime BaseElement = Copyable & Deinitable & Writable trait Test: comptime Element: RegisterPassable comptime element_bitwidth = bit_width_of[Self.Element]() @fieldwise_init struct SampleStruct[T: BaseElement](Test): comptime Element = Int64 var x: Self.T def show_element_bitwidth(self): print(Self.element_bitwidth) def main(): var s = SampleStruct[Int64](x=42) s.show_element_bitwidth() # 64 print(s.x) # 42 ``` :::note Don't use traits to define general-purpose constants like `PI` or `SPEED_OF_LIGHT`. Define them at the top level of a module for local use, or as a public member of a related struct for broader use. ::: ### Comptime members: required values 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. In this example, the trait requires conforming types to provide a `unit` string, an `always_positive` boolean, and a `get_value()` method. The `validate()` function uses those requirements to check that the value is positive when `always_positive` is `True`: ```mojo trait Measurable: comptime unit: StaticString # Required comptime always_positive: Bool # Required def get_value(self) -> Float64: ... # Required method def validate[T: Measurable](measurement: T) raises: comptime if T.always_positive: if Float64(measurement.get_value()) < 0.0: raise Error(t"{T.unit} cannot be negative") @fieldwise_init struct Pascals(Measurable): comptime unit: StaticString = "Pa" comptime always_positive: Bool = True var value: Float64 def get_value(self) -> Float64: return self.value def main() raises: validate(Pascals(value=101325.0)) # Validation succeeds # validate(Pascals(value=-101325.0)) # Validation fails # run-time error: # "Unhandled exception caught during execution: Pa cannot be negative" ``` A similar conformance for `DegreesCentigrade` would set the `unit` to `"°C"` and `always_positive` to `False`. A value of `-10.0` would pass validation for `DegreesCentigrade` and fail for `Pascals`. ## Trait refinement When a trait refines another, its constraint includes the parent's constraint: ```mojo trait Printable: def to_string(self) -> String: ... trait PrettyPrintable(Printable): def to_pretty_string(self) -> String: ... @fieldwise_init struct Box[T: Copyable & Writable & Deinitable](PrettyPrintable): var value: Self.T def to_string(self) -> String: return String(t"Box({self.value})") def to_pretty_string(self) -> String: return String(t"Box with value: {self.value}") def render[T: PrettyPrintable](item: T): # to_string() available: PrettyPrintable refines Printable print(item.to_string(), "-", item.to_pretty_string()) def main(): render(Box(1)) # Box(1) - Box with value: 1 render(Box("hello")) # Box(hello) - Box with value: hello ``` A function requiring `T: PrettyPrintable` can call any method from `Printable` without naming it in the constraint. ### Constraint resolution order When the compiler resolves a trait constraint: - The parameter's declared traits are checked first. - Parent traits are included transitively. - If the constraint uses `&`, all composed traits must be satisfied. - If a `where` clause is present, its constraints are checked after parameter-level constraints. If any check fails, the compiler reports which trait the type doesn't conform to. A child trait can override a parent method by declaring a method with the same signature. The parent's version is replaced in the child's requirements. :::note Every trait implicitly refines `AnyType`. ::: ## `Some[]` as constraint sugar `Some[Trait(s)]` is shorthand for a type parameter constrained to that trait or composition. It's syntax sugar, not a new mechanism. It moves the constraint onto the argument instead of declaring a parameter in one place and using it in another. The compiler infers the concrete type at the call site, just as it does for an explicit parameter. If inference fails, the compiler asks for an explicit parameter instead. | Context | With a parameter | With `Some` | |---------------------|--------------------------------------------------|-----------------------------------------------| | Argument | `def foo[T: Intable, //](x: T)` | `def foo(x: Some[Intable])` | | Function type | `def f[F: def(Int) -> None](func: F)` | `def f(func: Some[def(Int) -> None])` | | Variadic | `def show[*Ts: Writable](*pack: *Ts)` | `def show(*pack: *SomeTypeList[Writable])` | | Operator overload | `def __getitem__[I: Indexer, //](self, idx: I)` | `def __getitem__(self, idx: Some[Indexer])` | ### Where `Some` doesn't work The compiler can't infer a concrete type for a struct field: ```mojo @fieldwise_init struct Struct(Writable): var x: Some[Copyable & Deinitable & Writable] # Error: a `Some` struct field has no concrete type to infer ``` Use an explicit type parameter instead: ```mojo @fieldwise_init struct Struct[T: Copyable & Deinitable & Writable](Writable): var x: Self.T ``` ## Trait restrictions Traits don't support parameter lists: ```mojo trait Unsupported[T]: # Error: trait declarations do not support ... # parameters ``` Traits can't declare or use fields: ```mojo trait Unsupported: var x: Int # Error: traits do not support 'var' fields ``` Traits don't support `where` clauses on methods: ```mojo trait Unsupported: def maybe(self) -> Int where conforms_to(Self, Sized): ... # Error: 'where' clauses on trait methods are not supported ``` ## Conformance checks The compiler checks every requirement and errors on unmet ones. ### Missing methods ```mojo trait Sized: def __len__(self) -> Int: ... @fieldwise_init struct SizedStruct[T: Copyable](Sized): var backing_store: List[Self.T] # Error about missing a trait's required function '__len__'. ``` ### Missing required members ```mojo trait Container: comptime Element: Copyable @fieldwise_init struct Bag(Container): var data: Int # Error: 'Bag' does not implement all requirements for # 'Container' # Note: required member 'Element' is not specified ``` ### Type mismatch on associated types ```mojo trait Taggable: comptime Tag: Sized @fieldwise_init struct Widget(Taggable): comptime Tag = Bool # Error since Bool is not Sized ``` ### Provided method conflicts When two traits in a struct's conformance list produce conflicting provided methods for the same method, the struct must implement it manually: ```mojo trait Greeter: def greet(self): print("Hello from Greeter") trait Welcomer: def greet(self): print("Welcome from Welcomer") @fieldwise_init struct Host(Greeter, Welcomer): pass # Error about a trait method requirement greet having conflicting # default implementations in Greeter and Welcomer; you must # implement it manually ``` --- ## Mojo types reference Mojo is statically typed. Every value has a type that is known at compile time. This page catalogs built-in types that are available in every program without an import. ## Built-in types and the prelude Built-in types come from the standard library *prelude*, which the compiler imports into every program. The prelude consists of the `builtin` package (`Int`, `Bool`, `Error`, and core traits) plus selected types from `collections`, `memory`, and `math`. Although they feel like part of the language itself, built-in types are ordinary structs defined in the standard library, just like a type you might write yourself. The prelude and compiler syntax are separate concerns. Some language constructs have *syntax* without a corresponding name in scope. For example, a set *display* doesn't require an import but the `Set` type does: ```mojo var primes = {2, 3, 5, 7} # set display: compiler syntax, no import from std.collections import Set var empty = Set[Int]() # the name Set must be imported ``` ## Numeric types Mojo's numeric types are built on `SIMD`, a fixed-size, homogeneous vector of primitive values. `Int`, the sized integer types (`Int8` through `Int256` and `UInt8` through `UInt256`), and the floating-point types (`Float16` through `Float64`, `BFloat16`, and the `Float8_*` formats) are all available through the prelude. For details on sizes, precision, overflow behavior, and conversions, see [Numeric types](/docs/reference/numeric-types/). ## String types All string types hold UTF-8 encoded text. Their bytes are guaranteed to be valid UTF-8. Construction enforces this. `String(from_utf8_lossy=...)` replaces invalid bytes, and `String(unsafe_from_utf8=...)` requires the caller to guarantee validity. | Type | What it is | |--------------------------------------------------------------------------|----------------------------------------------| | [`String`](/docs/std/collections/string/string/String/) | Owned, mutable, heap-allocated UTF-8 string. | | [`StringSpan`](/docs/std/collections/string/string_span/StringSpan/) | Non-owning view into existing UTF-8 data. | | [`StaticString`](/docs/std/collections/string/string_span/#staticstring) | A `StringSpan` over static, read-only data. | | [`StringLiteral`](/docs/std/builtin/string_literal/StringLiteral/) | Compile-time string constant from source. | | [`Codepoint`](/docs/std/collections/string/codepoint/Codepoint/) | A single Unicode codepoint. | A string literal in source is a `StringLiteral`. It materializes to a `String` at runtime, or to a `StringSpan` when the context requires a view. `StringSlice` remains available as a compatibility alias for `StringSpan`. For details, see [String literals](/docs/reference/literals/#string-literals). Length has three measurements, and they disagree for non-ASCII text: ```mojo var wave = String("👋🏽") # waving hand + skin-tone modifier print(wave.byte_length()) # 8 print(wave.count_codepoints()) # 2 print(wave.count_graphemes()) # 1 ``` `byte_length()` counts UTF-8 bytes, `count_codepoints()` counts Unicode codepoints, and `count_graphemes()` counts user-perceived characters. Pick the count that matches the question being asked. ## Collection types Mojo includes a flexible set of collection types. | Type | What it is | |------------------------------------------------------------------|-------------------------------------------------------------| | [`List`](/docs/std/collections/list/List/) | Dynamically sized, growable sequence. | | [`Dict`](/docs/std/collections/dict/Dict/) | Key-value mapping. | | [`Set`](/docs/std/collections/set/Set/) | Unordered collection of unique values. Requires an import. | | [`Optional`](/docs/std/collections/optional/Optional/) | A value that may or may not be present. | | [`Tuple`](/docs/std/builtin/tuple/Tuple/) | Fixed-size, heterogeneous group of values. | | [`Array`](/docs/std/collections/array/Array/) | Fixed-size array stored inline, with no heap allocation. | | [`Variant`](/docs/std/utils/variant/Variant/) | Holds one value from a fixed set of types. Requires import. | `List`, `Dict`, `Set`, and `Tuple` support display syntax, as described in [Expressions](/docs/reference/expressions/#collection-displays). `Set` and `Variant` aren't in the prelude. Import `Set` from `std.collections` and `Variant` from `std.utils`. ### Optional An `Optional[T]` holds a `T` or nothing. It's truthy when a value is present: ```mojo var maybe: Optional[Int] = 5 if maybe: print(maybe.value()) # 5 ``` `value()` aborts on empty `Optional`s. Guard it with a truthiness check, or call `or_else()` to supply a default: ```mojo var empty: Optional[Int] = None # empty.value() # aborts: the Optional is empty print(empty.or_else(0)) # 0 ``` ### Variant A `Variant` holds one value from a fixed set of types, tracked at runtime. Test the active type with `isa[T]()`, and read it by indexing with that type: ```mojo from std.utils import Variant var v = Variant[Int, String](5) print(v[Int]) # 5 v.set[String]("text") print(v[String]) # text ``` ## Memory types Mojo memory types are pointers and views that provide non-owning access to memory. A single `Pointer` type covers both safe and unsafe use: rather than splitting the guarantees across two types, Mojo marks unsafety on the individual *operation*. Dereferencing a pointer is safe, while operations with requirements the compiler can't check for you, such as unchecked offsets, aliasing casts, and overwriting memory, carry an `unsafe_` prefix. Every `Pointer` is non-nullable; use `Optional[Pointer]` to model a pointer that may be absent. | Type | What it is | |----------------------------------------------------------------|----------------------------------------------------| | [`Pointer`](/docs/std/memory/pointer/Pointer/) | Non-nullable pointer to one or more values. | | [`Span`](/docs/std/collections/span/Span/) | Non-owning view of contiguous data. | | [`AddressSpace`](/docs/std/memory/address_space/AddressSpace/) | Identifies where memory lives, such as CPU or GPU. | `OpaquePointer`, `OptionalPointer`, and the `MutX` and `ImmX` forms are aliases of `Pointer`. `UnsafePointer` is a deprecated alias for `Pointer`, kept for code written before the two types were unified. For an overview of pointer types, see [Intro to pointers](/docs/manual/pointers/). For memory allocation and pointer usage, see [Using pointers](/docs/manual/pointers/using-pointers). ## Other built-in types Mojo includes several built-in types that don't fit into the above categories but are still fundamental to the language: | Type | What it is | |---------------------------------------------------|-----------------------------------------------------------| | [`Bool`](/docs/std/builtin/bool/Bool/) | Boolean value, `True` or `False`. Backed by a 1-bit type. | | [`Error`](/docs/std/builtin/error/Error/) | A runtime error raised by a `raises` function. | | [`Never`](/docs/std/builtin/type_aliases/#never) | The type of an expression that never produces a value. | | [`NoneType`](/docs/std/builtin/none/NoneType/) | The type whose only value is `None`. | | [`Slice`](/docs/std/builtin/builtin_slice/Slice/) | The `start:end:step` descriptor a subscript produces. | ### Bool A `Bool` struct is backed by a 1-bit value, not a `SIMD` alias. Its literals are `True` and `False`. Any type that conforms to `Boolable` provides a `Bool` representation that can act as a condition: ```mojo var flag = True if flag: print("yes") # yes ``` ### Error `Error` is Mojo's default error type. A function marked `raises` raises `Error` unless it declares another type (`raises T`): ```mojo def parse(s: String) raises -> Int: raise Error("invalid input") ``` ### Never `Never` is the type of an expression that never produces a value, such as a call that always aborts or a loop that never terminates. Values of type `Never` can't exist. The compiler treats any code that follows as unreachable. `Never` is a compiler type (`!kgen.never`), not a `struct`. ```mojo from std.os import abort def fatal() -> Never: abort() # never returns ``` ### None `None` is the only value of type `NoneType`. A function with no declared return type returns `None`: ```mojo def implicit_greet(name: String): print("hello", name) # returns None implicitly def explicit_greet(name: String) -> None: print("hello", name) # returns None explicitly ``` ### Slice A `Slice` holds `start`, `end`, and `step`, each an `Optional[Int]`. It's the descriptor that subscript syntax with colons produces; it carries no data of its own. What subscripting returns depends on the type being indexed. A contiguous `List` slice yields a `Span` view; a strided slice yields a new `List`. ```mojo var items: List[Int] = [0, 1, 2, 3, 4, 5] var middle = items[1:4] # Span view, [1, 2, 3] var strided = items[::2] # new List, [0, 2, 4] ``` For slice syntax, see [Expressions](/docs/reference/expressions/#slices).