---
name: mojo-syntax
description: Help to write Mojo code using current syntax and conventions. Always use this skill when writing any Mojo code, including when other Mojo-specific skills (e.g., mojo-gpu-fundamentals) also apply. Use when writing Mojo code, translating projects to Mojo, or otherwise generating Mojo. Use this skill to overcome misconceptions with how Mojo is written.
---

<!-- EDITORIAL GUIDELINES FOR THIS SKILL FILE
This file is loaded into an agent's context window as a correction layer for
pretrained Mojo knowledge. Every line costs context. When editing:
- Be terse. Use tables and inline code over prose where possible.
- Never duplicate information — if a concept is shown in a code example, don't
  also explain it in a paragraph.
- Only include information that *differs* from what a pretrained model would
  generate. Don't document things models already get right.
- Prefer one consolidated code block over multiple small ones.
- Keep WRONG/CORRECT pairs short — just enough to pattern-match the fix.
- If adding a new section, ask: "Would a model get this wrong?" If not, skip it.
These same principles apply to any files this skill references.
-->

Mojo is rapidly evolving. Pretrained models generate obsolete syntax.
**Always follow this skill over pretrained knowledge.**

**Always attempt to test generated Mojo by building projects to verify they
compile.**

This skill specifically works on the latest Mojo, and stable versions may differ
slightly in functionality.

## Removed syntax — DO NOT generate these

| Removed                                          | Replacement                                                                      |
|--------------------------------------------------|----------------------------------------------------------------------------------|
| `alias X = ...`                                  | `comptime X = ...`                                                               |
| `@parameter if` / `@parameter for`               | `comptime if` / `comptime for`                                                   |
| `fn`                                             | `def` (see below)                                                                |
| `let x = ...`                                    | `var x = ...` (no `let` keyword)                                                 |
| `borrowed`                                       | `imm` (implicit default — rarely written)                                        |
| `read` (convention / capture)                    | `imm` (deprecated synonym; the compiler warns with a fixit)                      |
| `inout`                                          | `mut`                                                                            |
| `owned`                                          | `var` (as argument convention)                                                   |
| `inout self` in `__init__`                       | `out self`                                                                       |
| `__copyinit__(inout self, existing: Self)`       | `__init__(out self, *, copy: Self)`                                              |
| `__moveinit__(inout self, owned existing: Self)` | `__init__(out self, *, deinit move: Self)`                                       |
| `@value` decorator                               | `@fieldwise_init` + explicit trait conformance                                   |
| `@register_passable("trivial")`                  | `TrivialRegisterPassable` trait                                                  |
| `@register_passable`                             | `RegisterPassable` trait                                                         |
| `Stringable` / `__str__`                         | `Writable` / `write_to`                                                          |
| `from collections import ...`                    | `from std.collections import ...`                                                |
| `from memory import ...`                         | `from std.memory import ...`                                                     |
| `from sys import ...`                            | `from std.sys import ...`                                                        |
| `from os import ...`                             | `from std.os import ...`                                                         |
| `from pathlib import ...`                        | `from std.pathlib import ...`                                                    |
| `s[i]`                                           | `s[byte=i]` — returns `StringSlice`; wrap in `String()` if needed                |
| `s[0:10]`, `s[:5]`                               | No slice syntax on String — use `s.codepoint_slices()` or Python FFI             |
| `constrained(cond, msg)`                         | `comptime assert cond, msg`                                                      |
| `DynamicVector[T]`                               | `List[T]`                                                                        |
| `InlinedFixedVector[T, N]`                       | `Array[T, N]`                                                                    |
| `Tensor[T]`                                      | Not in stdlib (use SIMD, List, UnsafePointer)                                    |
| `escaping` closures                              | Unified closures (`def(...) -> T`, captures in `{}`); `capturing[_]` still valid |
| `__del__(deinit self)`                           | `__deinit__(deinit self)`                                                        |

## `var` is required for every new declaration

Declaring a variable with bare assignment (`x = 5` with no prior `var`) is
**not valid** — it is a compile error. This applies only to introducing a new
variable; reassigning an already-declared variable (`x = 6`) needs no `var`.

This means a variable assigned only inside conditional branches must be
predeclared with a type before the branch:

```mojo
# WRONG — no prior `var`, so this doesn't declare `x`
if cond:
    x = 1
else:
    x = 2

# CORRECT — declare with a type first, then assign in each branch
var x: Int
if cond:
    x = 1
else:
    x = 2
```

## `def` is the only function keyword

`fn` was **removed** and is now a hard parse error — no valid use of `fn`
remains. Your training predates this, so you will reach for `fn` by reflex; that
reflex is always wrong. Write **every** function, method, and nested function as
`def`, without exception.

## Mojo functions that raise must be marked as such

Mojo functions do **not** imply `raises`. Add `raises` to any function that can
raise, directly or by calling a raising function. Omitting it is a **compile
error**, not a warning.

```mojo
def load(path: String) raises -> String:  # raises goes before the `->`
    return open(path).read()

def main() raises:                         # main usually raises
    ...
```

## `comptime` replaces `alias` and `@parameter if`/`for`

```mojo
comptime N = 1024                            # compile-time constant
comptime MyType = Int                        # type alias
comptime if condition:                       # compile-time branch
    ...
comptime for i in range(10):                 # compile-time loop
    ...
comptime assert N > 0, "N must be positive"  # compile-time assertion
```

**`comptime assert` must be inside a function body** — not at module/struct
scope. Place them in `main()`, `__init__`, or the function that depends on the
invariant.

Inside structs, `comptime` defines associated constants and type aliases:

```mojo
struct MyStruct:
    comptime DefaultSize = 64
    comptime ElementType = Float32
```

## Argument conventions

Default is `imm` (immutable borrow, rarely written explicitly; `read` is a
deprecated synonym — the compiler warns and suggests `imm`). The others:

```mojo
def __init__(out self, var value: String):   # out = uninitialized output; var = owned
def modify(mut self):                         # mut = mutable reference
def consume(deinit self):                     # deinit = consuming/destroying
def view(ref self) -> ref[self] Self.T:       # ref = reference with origin
def view2[origin: Origin, //](ref[origin] self) -> ...:           # ref[origin] = explicit origin
```

`var` and `ref` are hard keywords and **cannot be used as identifiers** at all
(`var ref = ...` → `"unexpected token in expression"`). The convention words
`imm`, `read`, `mut`, `out`, `deinit` are soft keywords: fine as local variable
or `[...]` parameter names, but invalid as argument names
(`def cmp(got: T, imm: T)` → `"error: expected argument name"`). Rename
(`expected`, `reference`, etc.).

## Lifecycle methods

```mojo
# Constructor
def __init__(out self, x: Int):
    self.x = x

# Copy constructor (keyword-only `copy` arg)
def __init__(out self, *, copy: Self):
    self.data = copy.data

# Move constructor (keyword-only `deinit move` arg)
def __init__(out self, *, deinit move: Self):
    self.data = move.data^

# Destructor
def __deinit__(deinit self):
    self.ptr.free()
```

To copy: `var b = a.copy()` (provided by `Copyable` trait).

## Struct patterns

```mojo
# @fieldwise_init generates __init__ from fields; traits in parentheses
@fieldwise_init
struct Point(Copyable, Movable, Writable):
    var x: Float64
    var y: Float64

# Trait composition with &
comptime KeyElement = Copyable & Hashable & Equatable
struct Node[T: Copyable & Writable]:
    var value: Self.T          # Self-qualify struct parameters

# Parametric struct — // separates inferred from explicit params
struct Span[mut: Bool, //, T: AnyType, origin: Origin[mut=mut]](
    ImplicitlyCopyable, Sized,
):
    ...

# @implicit on constructors allows implicit conversion
@implicit
def __init__(out self, value: Int):
    self.data = value
```

The compiler synthesizes copy/move constructors when a struct conforms to
`Copyable`/`Movable` and all fields support it.

### Self-qualify struct parameters

Inside a struct body, **always** use `Self.ParamName` — bare parameter names are
errors:

```mojo
# WRONG — bare parameter access
struct Container[T: Writable]:
    var data: T                        # ERROR: use Self.T
    def size(self) -> T:                # ERROR: use Self.T

# CORRECT — Self-qualified
struct Container[T: Writable]:
    var data: Self.T
    def size(self) -> Self.T:
        return self.data
```

This applies to all struct parameters (`T`, `N`, `mut`, `origin`, etc.)
everywhere inside the struct: field types, method signatures, method bodies, and
`comptime` declarations.

### Explicit copy / transfer

Types not conforming to `ImplicitlyCopyable` (e.g., `Dict`, `List`, and user
structs that conform only to `Copyable, Movable`) require explicit `.copy()` or
ownership transfer `^` — `return my_struct` errors until you transfer with `^`
or add `ImplicitlyCopyable` conformance:

```mojo
# WRONG — implicit copy of non-ImplicitlyCopyable type
var d = some_dict
var result = MyStruct(headers=d)   # ERROR

# CORRECT — explicit copy or transfer
var result = MyStruct(headers=d.copy())  # or: headers=d^
```

## Imports use `std.` prefix

```mojo
from std.testing import assert_equal, TestSuite
from std.algorithm import vectorize
from std.python import PythonObject
import std.random
```

Prelude auto-imports (no import needed): `Int`, `String`, `Bool`, `List`,
`Dict`, `Optional`, `SIMD`, `Float32`, `Float64`, `UInt8`, `Pointer`,
`UnsafePointer`, `Span`, `Error`, `DType`, `Writable`, `Writer`, `Copyable`,
`Movable`, `Equatable`, `Hashable`, `rebind`, `print`, `range`, `len`, and more.

`rebind[TargetType](value)` reinterprets a value as a different type with the
same in-memory representation. Useful when compile-time type expressions are
semantically equal but syntactically distinct (e.g., TileTensor element types
— see GPU skill).

`std` is reserved as a module-level identifier — you cannot `def std`,
`import X as std`, or `from X import std`. Struct methods named `std` are fine.

Inside a multi-module package, `pkg.X.Y(...)` from a submodule needs explicit
`import pkg`; `import pkg.X as X` binds only `X`, not `pkg`.

## `Writable` / `Writer` (replaces `Stringable`)

```mojo
struct MyType(Writable):
    var x: Int

    def write_to(self, mut writer: Some[Writer]):       # for print() / String()
        writer.write("MyType(", self.x, ")")

    def write_repr_to(self, mut writer: Some[Writer]):   # for repr()
        t"MyType(x={self.x})".write_to(writer)           # t-strings for interpolation
```

- `Some[Writer]` — builtin existential type (not `Writer` directly)
- Both methods have **default implementations** via reflection if all fields are
  `Writable` — simple structs need not implement them
- Convert to `String` with `String(value)`, not `str(value)`

## Iterator protocol

Iterators use `raises StopIteration` (not `Optional`):

```mojo
struct MyCollection(Iterable):
    comptime IteratorType[
        iterable_mut: Bool, //, iterable_origin: Origin[mut=iterable_mut]
    ]: Iterator = MyIter[origin=iterable_origin]

    def __iter__(ref self) -> Self.IteratorType[origin_of(self)]: ...

# Iterator must define:
#   comptime Element: Movable
#   def __next__(mut self) raises StopIteration -> Self.Element
```

For-in: `for item in col:` (immutable) / `for ref item in col:` (mutable).

## Memory and pointer types

| Type                            | Use                                                                    |
|---------------------------------|------------------------------------------------------------------------|
| `Pointer[T, mut=M, origin=O]`   | Safe, non-nullable. Deref with `p[]`.                                  |
| `alloc[T](n)` / `UnsafePointer` | Free function `alloc[T](count)` → `UnsafePointer`. `.free()` required. |
| `Span(list)`                    | Non-owning contiguous view.                                            |
| `OwnedPointer[T]`               | Unique ownership (like Rust `Box`).                                    |
| `ArcPointer[T]`                 | Reference-counted shared ownership.                                    |

`UnsafePointer` has an `origin` parameter that must be specified for struct
fields. Use `MutUntrackedOrigin` for owned heap data (this is what stdlib
`ArcPointer` uses):

```mojo
# Struct field — specify origin explicitly
var _ptr: UnsafePointer[Self.T, MutUntrackedOrigin]

# Allocate with alloc[]
def __init__(out self, size: Int):
    self._ptr = alloc[Self.T](size)
```

`UnsafePointer` is **non-null by design** — null default constructor and
`__bool__` are deprecated. For nullable storage, use
`Optional[UnsafePointer[...]]` (same layout; `None` is the null niche).

## Origin system (not "lifetime")

Mojo tracks reference provenance with **origins**, not "lifetimes":

```mojo
struct Span[mut: Bool, //, T: AnyType, origin: Origin[mut=mut]]: ...
```

Key types: `Origin`, `MutOrigin`, `ImmOrigin`, `MutAnyOrigin`,
`ImmutAnyOrigin`, `MutUntrackedOrigin`, `ImmUntrackedOrigin`,
`ImmStaticOrigin`. Use `origin_of(value)` to get a value's origin.

## Testing

```mojo
from std.testing import assert_equal, assert_true, assert_false, assert_raises, TestSuite

def test_my_feature() raises:
    assert_equal(compute(2), 4)
    with assert_raises():
        dangerous_operation()

def main() raises:
    TestSuite.discover_tests[__functions_in_module()]().run()
```

The `mojo test` CLI subcommand was removed — run test files with `mojo run`
against a `TestSuite.discover_tests` runner like the one above.

## Dict iteration

Dict entries are iterated directly — no `[]` deref:

```mojo
for entry in my_dict.items():
    print(entry.key, entry.value)      # direct field access, NOT entry[].key

for key in my_dict:
    print(key, my_dict[key])
```

## Collection literals

`List` has **no variadic positional constructor**. Use bracket literal syntax:

```mojo
# WRONG — no List[T](elem1, elem2, ...) constructor
var nums = List[Int](1, 2, 3)

# CORRECT — bracket literals
var nums = [1, 2, 3]                              # List[Int]
var nums: List[Float32] = [1.0, 2.0, 3.0]         # explicit element type
var scores = {"alice": 95, "bob": 87}              # Dict[String, Int]
```

`List[T]` rejects negative indices at compile time — use `lst[len(lst) - 1]`,
not `lst[-1]`. (Library types may still support it.)

## Variant access

`Variant[A, B]` is `ImplicitlyCopyable` only if *all* arms are. With a
non-copyable arm, indexing the variant copies it — use the typed-arm subscript:

```mojo
# WRONG — `values[i]` implicitly copies the Variant
var x = values[i].unwrap[T]()    # ERROR: cannot implicitly copy

# CORRECT — `values[i][T]` returns a ref to the inner value
var x = values[i][T].copy()          # or `^` to transfer
```

## Common decorators

| Decorator                                      | Purpose                               |
|------------------------------------------------|---------------------------------------|
| `@fieldwise_init`                              | Generate fieldwise constructor        |
| `@implicit`                                    | Allow implicit conversion             |
| `@always_inline` / `@always_inline("nodebug")` | Force inline                          |
| `@no_inline`                                   | Prevent inline                        |
| `@staticmethod`                                | Static method                         |
| `@deprecated("msg")`                           | Deprecation warning                   |
| `@doc_hidden`                                  | Hide from docs                        |
| `@explicit_destroy`                            | Linear type (no implicit destruction) |

## Numeric conversions — must be explicit

No implicit conversions between numeric *variables*. Use explicit constructors:

```mojo
var x = Float32(my_int) * scale    # CORRECT: Int → Float32
var y = Int(my_uint)               # CORRECT: UInt → Int
```

**Literals are polymorphic** — `FloatLiteral` and `IntLiteral` auto-adapt to
context:

```mojo
var a: Float32 = 0.5              # literal becomes Float32
var b = Float32(x) * 0.003921    # literal adapts — no wrapping needed
var v = SIMD[DType.float32, 4](1.0, 2.0, 3.0, 4.0)  # literals adapt
```

## SIMD operations

```mojo
# Construction and lane access
var v = SIMD[DType.float32, 4](1.0, 2.0, 3.0, 4.0)
v[0]                              # read lane → Scalar[DType.float32]
v[0] = 5.0                        # write lane

# Type cast
v.cast[DType.uint32]()            # element-wise → SIMD[DType.uint32, 4]

# Clamp (method)
v.clamp(0.0, 1.0)                 # element-wise clamp to [lower, upper]

# min/max are FREE FUNCTIONS, not methods
from std.math import min, max
min(a, b)                          # element-wise min (same-type SIMD args)
max(a, b)                          # element-wise max

# Element-wise ternary via bool SIMD
var mask = (v > 0.0)              # SIMD[DType.bool, 4]
mask.select(true_case, false_case) # picks per-lane

# Reductions
v.reduce_add()                     # horizontal sum → Scalar
v.reduce_max()                     # horizontal max → Scalar
v.reduce_min()                     # horizontal min → Scalar
```

## Strings

**All explicit stdlib imports require the `std.` prefix.** The
removed-syntax table shows the most common corrections, but the rule
is universal. Prelude types (`Int`, `String`, `List`, etc.) are
auto-imported and need no import statement.

`len(s)` returns **byte length**, not codepoint count. Mojo strings are UTF-8.
Byte indexing requires keyword syntax: `s[byte=idx]` (not `s[idx]`). `len(s)` is
deprecated on `String` — use `s.byte_length()` or `s.count_codepoints()`.

`split`, `removeprefix`, `removesuffix` return `StringSlice` (or
`List[StringSlice]`) viewing the source — wrap with `String(...)` to
materialize an owned `String`.

### String indexing (common error)

```mojo
# WRONG — compile error
var ch = s[0]
var sub = s[0:10]

# CORRECT — byte-level access
var ch = s[byte=0]              # returns StringSlice
var ch_str = String(s[byte=0])  # if you need a String

# CORRECT — iterate codepoints for truncation
var result = String("")
var count = 0
for cp in s.codepoint_slices():
    if count >= 10:
        break
    result += String(cp)
    count += 1
```

```mojo
var s = "Hello"
len(s)                  # 5 (bytes)
s.byte_length()         # 5 (same as len)
s.count_codepoints()    # 5 (codepoint count — differs for non-ASCII)

# Iteration — `for c in s:` is deprecated; use codepoint_slices()
for cp_slice in s.codepoint_slices():
    print(cp_slice)

# Codepoint values
for cp in s.codepoints():
    print(Int(cp))      # Codepoint is a Unicode scalar value type

# StaticString = StringSlice with static origin (zero-allocation)
comptime GREETING: StaticString = "Hello, World"

# t-strings for interpolation (lazy, type-safe)
var msg = t"x={x}, y={y}"

# String.format() for runtime formatting
var s = "Hello, {}!".format("world")
```

## Error handling

`raises` can specify a type. `try`/`except` works like Python:

```mojo
def might_fail() raises -> Int:          # raises Error (default)
    raise Error("something went wrong")

def parse(s: String) raises Int -> Int:  # raises specific type
    raise 42

try:
    var x = parse("bad")
except err:                               # err is Int
    print("error code:", err)
```

No `match` statement. `async def` and `await` parse, but async support is
unfinished and its types are private — do not write async Mojo yet.

## Function types and closures

No lambda. Closures use bare `def` with a capture list in `{}` after the arg
list. `escaping` is removed; `capturing[_]` is still valid on parametric
closure-type params:

```mojo
comptime MyFn = def(Int) -> None                  # unified value type
def runner[f: def(Int) capturing[_] -> None](): ...  # parametric form

def closure(i: Int) {mut count, imm ptr, var x}:  # captures: mut/imm/var
    count += ptr[i] + x^                          # `^` at use site, not in `{}`

vectorize[simd_width](size, closure)              # runtime-arg overload
```

`imm` is default. `var x` is owned — transfer with `x^` at the use site.
Prefer unified closures with a capture list. Do **not** under any circumstance
use `@__parameter` / `@parameter` on nested closures — that legacy form is
**forbidden** in new and migrated code (not a temporary bridge, not an
imm-borrow helper, not a still-capturing API workaround). Pass closures as
runtime arguments (`f(my_closure)`) rather than comptime parameters when
possible. If an API still requires a comptime `capturing[_]` function, use
`def … capturing` without `@__parameter`, or migrate that API — never put
`@__parameter` on the caller.

## Type hierarchy

```text
AnyType
  Deinitable                      — auto __deinit__; most types
  Movable                         — __init__(out self, *, deinit move: Self)
    Copyable                      — __init__(out self, *, copy: Self)
      ImplicitlyCopyable(Copyable, take)
    RegisterPassable(Movable)
      TrivialRegisterPassable(ImplicitlyCopyable, take, Movable, RegisterPassable)
```
