> For the complete Mojo documentation index, see [llms.txt](/llms.txt).
> Markdown versions of all pages are available by appending .md to any URL (e.g. /docs/manual/basics.md).

# Mojo function declarations reference

<!-- VERIFIED: TokenKinds.def, Signatures.h, Signatures.cpp,
     ParserStmts.cpp, ParserBase.h, ParserBase.cpp, ASTDecl.h,
     ASTDecl.cpp, ExprNode.h. Consulted: ExprNodes.h,
     ExprNodes.cpp, ParserExprs.cpp, ASTType.h, MojoDiags.h -->

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](https://mojolang.org/docs/reference/argument-list.md) -> ReturnType:
    body

def name(argument-list) raises -> ReturnType:
    body

def name[parameter-list](https://mojolang.org/docs/reference/argument-list.md)
    -> ReturnType where constraint:
    body

def name[parameter-list](https://mojolang.org/docs/reference/argument-list.md) 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
def clamp[T: Comparable & ImplicitlyCopyable](https://mojolang.org/docs/reference/val: T, lo: T, hi: T,.md) -> 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              | Inferred        |
| `/`    | 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 inferred parameters from named parameters. The
compiler deduces inferred parameters from call-site arguments:

```mojo
from std.reflection import reflect

def inferred_type[T: Writable, //](https://mojolang.org/docs/reference/value: T.md):
    print(t"Value is {value}. Type is {reflect[T].name()}.")

def main():
    inferred_type(5)       # Value is 5. Type is Int.
    inferred_type("Hello") # Value is Hello. Type is String.
```

Infer-only parameters can't be specified positionally:

```mojo
# Error: 'inferred_type' expects 0 positional parameters,
# but 1 was specified
inferred_type[Int](https://mojolang.org/docs/reference/5.md)
```

Keyword syntax bypasses this restriction:

```mojo
inferred_type[T=Int](https://mojolang.org/docs/reference/5.md) # OK: Value is 5. Type is Int.

# Error: value passed to 'value' cannot be converted from
# 'StringLiteral["Hello"]' to 'Int'
inferred_type[T=Int](https://mojolang.org/docs/reference/"Hello".md)
```

Inference isn't limited to infer-only parameters. With enough
context, the compiler can infer named parameters too:

```mojo
def add[T: Intable](https://mojolang.org/docs/reference/a: T, b: T.md) -> Int:
    return Int(a) + Int(b)

def main():
    print(t"Sum is {add[Int](https://mojolang.org/docs/reference/1, 2.md)}.")         # Explicit T
    print(t"Sum is {add(1, 2)}.")              # Inferred T
    print(t"Sum is {add[Float64](https://mojolang.org/docs/reference/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:

```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=": ")
    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: 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: required positional argument follows optional
# positional argument
# def bad(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](https://mojolang.org/docs/reference/x: T, y: T,.md) -> 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,
](https://mojolang.org/docs/reference/data: SIMD[DType.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[DType.float32, 16](https://mojolang.org/docs/reference/255.0)
    var sum = process[n=16](https://mojolang.org/docs/reference/data.md)
    print(t"Sum: {sum}")  # Sum: 4080.0
```

`where` clauses are only valid at the end of a declaration.
They're not valid inside parameter or argument lists:

```mojo
# Error: where clauses are not valid in parameter lists
def bad[n: Int where n > 0]():
    pass

# Error: where clauses are not valid in argument lists
def bad(x: Int where x > 0):
    pass
```

## Argument passing conventions

A *passing convention* controls how a 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: 'mut' arguments may not have defaults
def bad(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: 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: function cannot have both an 'out' argument
#        and an explicit result type
def bad(out result: Int) -> Int:
    result = 0
```

`out` arguments can't have defaults and can't be variadic.

### `deinit`

The function takes ownership and destroys the value. Required
for `self` in `__del__` and the `existing` argument in move
constructors:

```mojo
struct Resource:
    var handle: Int

    def __del__(deinit self):
        _release(self.handle)
```

Destructors can't raise. Trivial types can't define a
destructor.

### `ref`

Passes a reference with an explicit *origin* specifier. The
origin tracks where the reference came from:

```mojo
def get_first[T: Copyable](https://mojolang.org/docs/reference/ref data: List[T],.md) -> ref [origin_of(data)]T:
    return data[0]

def main():
    data = ["one", "two", "three"]
    first = get_first(data)
    print(first)  # one
```

### Default convention

Without a convention, the argument is an immutable read-only reference. The
caller keeps ownership:

```mojo
def length[T: Copyable](https://mojolang.org/docs/reference/s: List[T].md) -> Int:
    return len(s)
```

## Variadic arguments

Variadic arguments accept a varying number of values ("indefinite
arity"). This is seen in functions like `print` that can take any number of
arguments.

### 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](https://mojolang.org/docs/reference/*args: *Ts.md):
    comptime for idx in range(args.__len__()):
        print(args[idx], end=" ")
    print()

def main():
    print_all("Hello", 42, 3.14) # Hello 42 3.14
```

### Variadic restrictions

A function can have at most one `*args`. Variadic arguments
can't have default values:

```mojo
# Error: variadic arguments may not have defaults
def bad(*args: Int = 0):
    pass
```

`out` arguments can't be variadic:

```mojo
# Error: 'out' convention may not be variadic
def bad(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 ValueError -> Int:
    ...
```

A function can specify at most one error type after `raises`.

### `thin` {#thin}

Declares that a function type does not capture values from its defining
scope. A thin function is a function pointer and not a closure.

```mojo
def map[
    T: Copyable, U: Copyable
](https://mojolang.org/docs/reference/f: def(T.md) thin -> U, input: List[T]) -> List[U]:
    var result: List[U] = []
    for item in input:
        result.append(f(item))
    return result^

# `square` does not capture any values, so it is `thin`
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]
```

### `abi("C")` {#abi-c}

Declares that a function uses the C calling convention. Because C has no
closure mechanism, `abi("C")` always appears together with `thin`:

```mojo
from std.ffi import OwnedDLHandle

def main() raises:
    # On Linux. On macOS, use "libSystem.dylib", and so forth
    var lib = OwnedDLHandle("libm.so")
    var sqrt = lib.get_function[def(Float64) thin abi("C") -> Float64](https://mojolang.org/docs/reference/"sqrt".md)
    print(sqrt(4.0))  # 2.0
```

### `register_passable` {#register-passable}

Declares that a function value can be passed through registers rather
than by reference. A register-passable function has no heap captures
and no captures that require a stable memory address, making it
suitable for high-performance contexts like parallel execution and
GPU kernels.

```mojo
def apply[F: def(Int) register_passable -> Int](https://mojolang.org/docs/reference/f: F, x: Int.md) -> Int:
    return f(x)

def double(x: Int) register_passable -> Int:
    return x * 2

def main():
    print(apply(double, 21))  # 42
```

The type constraint
`def(Int) register_passable -> Int` on `F` enforces that callers can only
pass `register_passable` callables.

## 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.

### `__init__`

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: __init__ method must return Self type
#        with 'out' argument
def __init__(self):
    pass
```

### Copy constructor

A copy constructor is an `__init__` with a single
keyword-only argument named `copy`:

```mojo
def __init__(out self, *, copy: Self):
    self.x = copy.x
    self.y = copy.y
```

The `copy` argument must use the default convention of a readable immutable
reference. Copy constructors can't raise. Trivial types can't define a copy
constructor.

### Move constructor

A move constructor is an `__init__` with a single
keyword-only argument named `take`:

```mojo
def __init__(out self, *, deinit take: Self):
    self.x = take.x
    self.y = take.y
```

The `take` argument must use the `deinit` convention. Move
constructors can't raise.

:::note
`@register_passable` types can't define a move constructor.
They're always movable by copying a register.
:::

### `__del__`

The destructor takes `deinit self`:

```mojo
def __del__(deinit self):
    _release(self.handle)
```

Destructors can't raise. Trivial types can't define a
destructor.

## Nested functions

Functions can be defined inside other functions. Nested
functions capture values from the enclosing scope:

```mojo
def outer(x: Int) -> Int:
    def inner() {read} -> Int:
        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
```
