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

# Closures

:::caution Evolving feature
Closures are a longstanding part of Mojo, used throughout the standard
library and kernel infrastructure.

Mojo's updated capture-list syntax is now available. Code examples
on this page reflect the redesigned compiler behavior.
:::

A *closure* is a function bundled together with values from its surrounding
scope. You define it in one place and pass it somewhere else to run. The
closure carries captured data with it. The compiler transforms it to a type
with both behavior and storage. The code executing the closure doesn't
need to know where that data came from.

This allows closures to carry state and configuration into code that
executes later or elsewhere. Configuration uses capture conventions to
specify how the closure interacts with captured values, choosing between
read-only references, mutable references, copies, or moves.

Mojo closures look like nested functions, but they use a special
syntax. Curly braces after the argument list form a *capture list* that
declares which outer values the closure captures and how it interacts
with them.

The capture list is what distinguishes a closure from an ordinary nested
function. It gives the compiler the information needed to manage captured
values safely and eliminate ambiguity. When you specify how a value is
captured, the compiler can enforce correct usage:

```mojo
def main():
    var multiplier = 3

    def scale(x: Int) {read multiplier} -> Int:
        return x * multiplier

    print(scale(5))  # 15
```

`scale` is a closure. It captures `multiplier` from the enclosing
scope using its capture list (`{read multiplier}`). When you call
`scale(5)`, the closure multiplies `5` by the captured value of
`multiplier` and returns `15`.

Without a capture list, the inner function can't see anything outside its
own arguments.

## Why closures matter

Closures package behavior together with the data that behavior
needs. You define the work in one place, then pass that package for
execution later, elsewhere, or on different hardware.

This separation between defining work and executing work is central
to how Mojo expresses computation.

Closures already appear throughout the Mojo standard library.
Functions such as `parallelize`, `vectorize`, and `elementwise`
accept closures. You describe the work; the library decides how to
distribute it across SIMD lanes, threads, or cores.

GPU kernels extend the same idea. A closure captures the values a
kernel needs, then executes across a hardware dispatch geometry.
This pattern appears in reductions and accumulations, where
a closure captures running state and updates it while processing
elements. It also appears in custom iteration, where the iterator
controls traversal and the closure controls behavior.

In many languages, closures also power asynchronous programming. You
pass a closure as a callback that runs after an operation completes,
carrying the state and cleanup logic it needs.

Mojo doesn't yet support async execution or escaping closures
(closures that outlive their enclosing scope), but the underlying
model is the same: closures define what should happen, while
something else decides when and where it runs.

## The capture list

A capture list such as `{read x, mut y}` tells the compiler how the
closure captures and uses values from the surrounding scope.

For this list, `x` is captured as an immutable reference. The closure
can read it but can't modify it. `y` is captured as a mutable
reference, so the closure can modify it and those changes are visible
in the outer scope.

Mojo requires captures to be explicit. In a systems language, knowing
exactly which values a closure holds, and whether it reads, copies, or
takes ownership of them, matters for both performance and correctness.

## Capture by immutable reference: `read`

Use `read` when the closure needs to see a value but not change it:

```mojo
def main():
    var threshold = 100

    def is_over(x: Int) {read threshold} -> Bool:
        return x > threshold

    print(is_over(50))   # False
    print(is_over(200))  # True
```

The closure reads an immutable reference to `threshold`. It sees the
current value each time it's called, including changes made after the
closure was created:

```mojo
def main():
    var limit = 10

    def check(x: Int) {read limit} -> Bool:
        return x < limit

    print(check(5))   # True
    limit = 3
    print(check(5))   # False  (sees updated limit)
```

Because `read` captures a reference, the closure reflects the
live state of the original value.

To capture every used outer value by immutable reference without
naming each one, use `{read}`:

```mojo
def main():
    var a = 1
    var b = 2

    def sum_ab() {read} -> Int:
        return a + b

    print(sum_ab())  # 3
```

## Capture by mutable reference: `mut`

Use `mut` when a closure needs to modify a captured value and make
those changes visible in the enclosing scope:

```mojo
def main():
    var total = 0

    def accumulate(x: Int) {mut total}:
        total += x

    accumulate(10)
    accumulate(20)
    print(total)  # 30
```

Changes to `total` inside the closure modify the original variable
directly. This is a mutable reference, not a copy.

The implicit form `{mut}` captures every used outer value with a
mutable reference:

```mojo
def main():
    var count = 0
    var items = List[String]()

    def record(name: String) {mut}:
        items.append(name)
        count += 1

    record("alpha")
    record("beta")
    print(count)  # 2
    print(items)  # ['alpha', 'beta']
```

## Capture by copy: `var`

Use `var` when the closure needs its own independent copy of a
value. Changes to the original don't affect the closure, and
changes inside the closure don't affect the original.

```mojo
def main():
    var snapshot_val = 42

    def frozen() {var snapshot_val} -> Int:
        return snapshot_val

    snapshot_val = 999
    print(frozen())  # 42  (captured the value at definition time)
```

The closure copied `snapshot_val` when it was created. Later
changes to `snapshot_val` in the outer scope don't affect the
closure's copy.

The implicit form `{var}` copies every used outer value:

```mojo
def main():
    var x = 10
    var y = 20

    def snap() {var} -> Int:
        return x + y

    x = 0
    y = 0
    print(snap())  # 30  (uses copied values)
```

:::note
Copy captures call the value's copy constructor at the point
where the closure is defined. For types with expensive copies
(large lists, strings with allocations), prefer `read` or `mut`
when you don't need an independent copy.
:::

## Move capture: `var name^`

Use `var name^` to transfer ownership of a value into the closure.
The closure consumes the outer binding, which can't be used after
the closure is created:

```mojo
def main():
    var data: List[Int] = [1, 2, 3]

    def take_data() {var data^}:
        print(data)

    take_data()  # [1, 2, 3]
    # data can't be used here: ownership transferred to the closure
    # print(data)  # Uncomment for error: 'data' is uninitialized after move
```

Move capture avoids a copy entirely. The value moves into the
closure's storage. This is useful for types that are expensive to
copy or for transferring unique ownership.

:::note
The `^` transfer operator only works with `var` or a bare name in
capture lists. `{mut name^}`, `{ref name^}`, and `{read name^}` are
compiler errors.
:::

## Copyable closures: `var^`

The `{var^}` capture list moves all referenced outer values into the
closure. When the captured types are `Copyable`, the closure value also
becomes copyable.

This allows the closure itself to be assigned to new variables or
passed by value.

```mojo
def main():
    var label = "sensor-1"

    def tag() {var^} -> String:
        return label

    var also_tag = tag  # copies the closure (and its captures)
    print(tag())        # sensor-1
    print(also_tag())   # sensor-1
```

:::note
When you copy a closure created with `{var^}`, each captured value
is copied again through its copy constructor. For closures that
capture large or expensive values, be aware of the cost.
:::

Without `{var^}`, closures can't be assigned to new variables or
copied.

## Caller-determined mutability: `ref`

Use `{ref name}` when the closure's mutability depends on the
caller's context. If the caller provides a mutable reference, the
closure captures mutably. If immutable, the closure captures
immutably.

The following example uses `comptime if origin_of(items).mut` to inspect
how the closure captures the value at each call site:

```mojo
def show_mutability(ref items: List[Int]):
    def report() {ref items}:
        comptime if origin_of(items).mut:
            print("mut")
        else:
            print("immut")
    report()

# Show immutability: `xs` uses the default `read` argument convention
def from_read(xs: List[Int]):
    show_mutability(xs)  # xs is an immutable reference here

# Show mutability: `xs` uses the `mut` argument convention
def from_mut(mut xs: List[Int]):
    show_mutability(xs)  # xs is a mutable reference here

def main():
    var nums: List[Int] = [10, 20, 30]
    from_read(nums)   # immut
    from_mut(nums)    # mut
```

`{ref name}` doesn't choose a mutability. It shares the captured `name`'s
existing origin. The mutability is whatever that origin already carries,
decided wherever `name` was bound. This is often the function's own `ref`
parameter, ultimately resolved at the call site.

:::note
`ref` captures are an advanced feature for writing generic code
that works across mutability contexts. For most closures, `read`
or `mut` is the right choice.
:::

## Empty capture list: `{}`

An empty capture list means the closure uses nothing from its surrounding
scope. It's a plain function that happens to be defined inside
another function:

```mojo
def main():
    def doubled(x: Int) {} -> Int:
        return x * 2

    print(doubled(5))  # 10
```

The body may only use its own arguments. Referencing any outer value is a
compile error:

```mojo
# This example doesn't compile

def main():
    var a = 42

    def wrong() {}:
        print(a)        # error: no capture convention for 'a'
```

## Mixing capture conventions

A capture list is a comma-separated sequence of independent entries.
Each entry specifies its own convention, and conventions don't carry
over from one entry to the next.

```mojo
def main():
    var config = "prod"
    var count = 0
    var label = "run-1"

    def process() {read config, mut count, var label}:
        count += 1
        print(config, count, label)

    process()  # prod 1 run-1
    label = "run-2"
    process()  # prod 2 run-1  (label was copied at definition time)
```

Each entry is self-contained: `read config` is a read-only reference,
`mut count` is a mutable reference, and `var label` is a copy. A bare name
without a convention keyword defaults to `read`:

```mojo
def main():
    var x = 10

    def show() {x}:  # same as {read x}
        print(x)

    show()  # 10
```

### Setting a default convention

A convention list can mix implicit and explicit entries, such as
`{read, mut count, var label}`. You may use at most one implicit
entry per capture list, and you can place the entries in any order.
`{mut count, var label, read}` is equivalent to
`{var label, read, mut count}`.

For example:

```mojo
def main():
    var a = 1
    var b = 2
    var z = "snapshot"

    def mixed() {mut, var z}:
        a += 10
        b += 20
        print(a, b, z)

    mixed()   # 11 22 snapshot
    z = "changed"
    mixed()   # 21 42 snapshot
              # z was copied at def-time
              # Changes to outer z don't reach the closure
```

## Closures in practice

### Configurable behavior

Closures let you build specialized behavior from general-purpose parts.
The following generic function accepts any callable with the expected
signature. The closure carries the configuration:

```mojo
# `G` matches any `def(String) -> None` callable
def greet_all[G: def(String) -> None](https://mojolang.org/docs/manual/values/names: List[String], greet: G.md):
    for n in names:
        greet(n)

def main():
    var names: List[String] = ["Alice", "Bob"]
    var greeting = "Hello"

    def greeter(name: String) {read greeting}:
        print(greeting + ", " + name + "!")

    greet_all(names, greeter)
    # Hello, Alice!
    # Hello, Bob!

    greeting = "Hi"
    greet_all(names, greeter)
    # Hi, Alice!
    # Hi, Bob!
```

The inner function `greeter` captures `greeting` as an immutable read-only
reference.

`greet_all` doesn't know anything about the greeting itself. It only
knows how to call a function with the type
`def(String) -> None`.

Because the closure captures `greeting` by reference instead of by
copy, changes to `greeting` between calls are visible inside the
closure.

### Accumulating state

Closures with `mut` captures can build up results across
multiple calls.

```mojo
def main():
    var log = List[String]()

    def record(event: String) {mut log}:
        log.append(event)

    record("started")
    record("processed item")
    record("finished")

    for entry in log:
        print(entry)
    # started
    # processed item
    # finished
```

The closure `record` mutates `log` in the outer scope. Each call
appends to the same list without passing it as an argument.

### Separating logic from execution

The standard library uses closures to separate what you compute
from how it gets executed. You provide the logic; the library
handles parallelism, vectorization, or hardware dispatch.

```mojo
from std.algorithm import parallelize

def main():
    var results = List[Int](https://mojolang.org/docs/manual/values/length=8, fill=0.md)

    def work(i: Int) {mut results}:
        results[i] = i * i

    parallelize(work, 8)
    print(results)  # [0, 1, 4, 9, 16, 25, 36, 49]
```

You define `work` with the logic for a single element. `parallelize` calls
it across available threads. Each call to `work` accesses the same `results`
through mutable capture. The squared values land in the outer list, ready
to use after `parallelize` finishes.
