---
title: Closures
source: 'https://academia.sh/en/courses/swift/closures'
course: 'The Swift Language'
language: en
updated: '2026-10-21T05:03:11+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Write a closure in short syntax and as a trailing closure'
    - 'Show that a closure body runs on every call and that the body of a closure that is never called never runs'
    - 'Read the difference between a capture list and capturing without a list'
---

# Closures

Introduces closure syntax, the `$0` shorthand, the trailing closure and the `sorted(by:)` call; measures that a capture list is evaluated once, when the closure is created.

Functions are named behavior. A closure writes the same behavior without a name, and you can put it in a variable or give it to another function. The question of this lesson has two parts: when does the body of a closure run, and at which moment does the closure see the variables around it?

## Writing a Closure

The longest form writes the parameter and return types inside the curly braces: `{ (a: Int, b: Int) -> Bool in a < b }`. The `in` keyword separates the signature from the body. When the type can be inferred from the context the signature drops, and the parameters are reached by position: `$0` is the first and `$1` the second argument. If the body is a single expression, `return` is not written and the value of the expression is returned implicitly.

If the last parameter of a function is a closure, the closure can be written outside the parentheses. This is called a trailing closure. If no other argument is left inside the parentheses, the parentheses themselves drop too: `numbers.sorted { $0 > $1 }`. `sorted(by:)` sorts an array according to the comparison closure you give.

```swift
// Probe.swift — records when an expression is evaluated
final class Probe {
    let label: String
    private(set) var evaluations = 0
    private(set) var uses = 0
    private var evaluatedBeforeUse = false

    init(_ label: String) {
        self.label = label
    }

    func eval<T>(_ value: T) -> T {
        evaluations += 1
        if uses == 0 { evaluatedBeforeUse = true }
        return value
    }

    func use<T>(_ access: () -> T) -> T {
        uses += 1
        return access()
    }

    var moment: String {
        if evaluations == 0 { return "never" }
        if evaluations > 1 { return "on-every-use" }
        return evaluatedBeforeUse ? "immediately" : "on-first-use"
    }

    func report() {
        print("\(label): \(evaluations) evaluations, \(uses) uses -> \(moment)")
    }
}

func tally(_ probes: [Probe]) {
    for moment in ["immediately", "on-first-use", "on-every-use", "never"] {
        let count = probes.filter { $0.moment == moment }.count
        print("\(moment): \(count)")
    }
}
```

The measurement block first runs these forms: the long closure, the short closure with `$0`, `map` and our own `apply`. Then it builds two closures. `scale` carries a `Probe` in its body and is called twice; `unused` is built in the same way but is never called.

```swift
// main.swift — closure forms and the moment of the body
let numbers = [3, 1, 2]
print(numbers.sorted(by: { (a: Int, b: Int) -> Bool in a < b }))
print(numbers.sorted { $0 > $1 })
print(numbers.map { $0 * 2 })

func apply(_ n: Int, using transform: (Int) -> Int) -> Int { transform(n) }
print(apply(4) { $0 + 1 })

let bodyProbe = Probe("closure-body")
let unusedProbe = Probe("uncalled-body")

let scale = { (n: Int) -> Int in bodyProbe.eval(n * 2) }
let unused = { (n: Int) -> Int in unusedProbe.eval(n) }

print(bodyProbe.use { scale(1) })
print(bodyProbe.use { scale(2) })
_ = unusedProbe.use { unused }
_ = unusedProbe.use { unused }

let allProbes = [bodyProbe, unusedProbe]
for probe in allProbes { probe.report() }
tally(allProbes)
```

```
[1, 2, 3]
[3, 2, 1]
[6, 2, 4]
5
2
4
closure-body: 2 evaluations, 2 uses -> on-every-use
uncalled-body: 0 evaluations, 2 uses -> never
immediately: 0
on-first-use: 0
on-every-use: 1
never: 1
```

`sorted(by:)` gave the result from smallest to largest, and with `$0 > $1` from largest to smallest. `map` doubled every element, and `apply(4)` with a trailing closure returned 5. The measurement lines look at the body of the closure: `closure-body` was evaluated twice in two calls, that is, the body runs again on every call and the moment is `on-every-use`. `uncalled-body` showed zero evaluations in two uses: building a closure does not run the body, the body runs only when there is a call.

**LB53.** How many times the comparison closure of `sorted(by:)` is called was not counted in this lesson; the number depends on the sorting algorithm, and the lesson shows only the result.

**LB54.** The closure body was measured with a simple expression that returns only an `Int` and has no side effects; escaping closures are outside this lesson.

## Escaping Closure: Stored Work

If you give a closure to a function and the function stores the closure after it has finished, the type of the closure has to be marked with `@escaping`. This mark says the closure may escape from the call. A non-escaping closure (the default) disappears when the call ends. An escaping closure can be added to an array, assigned to a property and called later. The line that builds the closure does not do the work; the work is done when the closure is called.

The block below writes a queue class. `add` stores the closure, and `runAll` calls all the stored ones. The body of the closure writes to a `Probe`. The number of evaluations is printed after the closures are added to the queue and after they are run.

```swift
// Queue.swift — a stored closure
final class Queue {
    var jobs: [() -> Int] = []
    func add(_ job: @escaping () -> Int) {
        jobs.append(job)
    }
    func runAll() {
        for job in jobs { _ = job() }
    }
}
let queueProbe = Probe("stored-job")
let queue = Queue()
queue.add { queueProbe.eval(1) }
queue.add { queueProbe.eval(2) }
print(queueProbe.evaluations)
_ = queueProbe.use { queue.runAll() }
_ = queueProbe.use { queue.runAll() }
print(queueProbe.evaluations)
queueProbe.report()
```

```
0
4
stored-job: 4 evaluations, 2 uses -> on-every-use
```

When the two closures were added to the queue the counter was 0: adding did not run the body. After the two `runAll` calls the counter became 4, because in each call two closures ran. The closures were stored and evaluated their body again every time they were called; so the moment reads `on-every-use`.

An escaping closure is the way to postpone work. You read when the body will run not from the line that builds the closure, but from the line that calls it.

**LB55.** Stored closures were measured inside a class; the consequences of capturing `self` in an escaping closure are outside this lesson.

## Shorthand Argument Names

You can give the closure parameters names like `$0` and `$1`. This spares you from writing the parameter list when the type is inferred from the context. If the body is a single expression, the implicit return works too. The operators of the standard library can also be passed like a function: writing `numbers.sorted(by: >)` does the same job as `numbers.sorted { $0 > $1 }`. `reduce` takes a closure with two parameters, and these parameters become `$0` and `$1`.

The block below runs three shorthand forms on an array. `map` passes every element through a `Probe`, and a second `map` call is applied this time to four elements.

```swift
// Shorthand.swift — shorthand arguments
let mapProbe = Probe("map-body")
let numbers = [3, 1, 2]
print(numbers.sorted(by: >))
print(numbers.reduce(0) { $0 + $1 })
print(mapProbe.use { numbers.map { mapProbe.eval($0 * 10) } })
print(mapProbe.use { (numbers + [4]).map { mapProbe.eval($0 * 10) } })
mapProbe.report()
```

```
[3, 2, 1]
6
[30, 10, 20]
[30, 10, 20, 40]
map-body: 7 evaluations, 2 uses -> on-every-use
```

`sorted(by: >)` gave `[3, 2, 1]`. `reduce` computed the sum as 6. The two `map` calls produced the arrays `[30, 10, 20]` and `[30, 10, 20, 40]`. The `Probe` showed seven evaluations: three in the first call and four in the second. The closure body ran once for every element. So the moment reads `on-every-use`.

Shorthand names are readable when the body is short; when the body grows, a named parameter keeps the meaning. Besides `$0` and `$1`, passing the operator directly is the shortest form, but it works only when the signature of the operator matches the signature of the closure.

**LB56.** Shorthand names were measured with an `Int` array; `$2` and beyond, and the meaning of `$0` in nested closures, are outside this lesson.

## A Closure Can Change the Variable It Captures

If the captured variable is a `var`, the closure does not only read it, it can change it. The closure holds not a copy of the variable but the variable itself. If several closures capture the same variable, a change made by one closure changes the value the other sees. For this reason captured `var` variables behave like shared state, and the lifetime of the variable extends together with the closures that capture it.

The block below captures a `hits` counter with two closures. `record` increases the counter by one and passes the value through a `Probe`; `skip` increases the counter by two directly. The closures are called in the order `record`, `skip`, `record`.

```swift
// Shared.swift — a shared captured variable
let sharedProbe = Probe("shared-counter")
var hits = 0
let record = {
    hits += 1
    return sharedProbe.eval(hits)
}
let skip = { hits += 2 }
print(sharedProbe.use { record() })
skip()
print(sharedProbe.use { record() })
print(hits)
sharedProbe.report()
```

```
1
4
4
shared-counter: 2 evaluations, 2 uses -> on-every-use
```

`record` returned 1 on the first call, and 4 on the second call after `skip` had made the counter 3; the last read confirmed that the value of `hits` is 4. The two closures saw the same variable: `record` saw the change `skip` made on the second call. Two calls brought two evaluations, and the moment is `on-every-use`.

Shared state gives flexibility, but makes it harder to follow which closure changed the variable and when. When the state is small and close by this is no problem; as it grows, putting the state inside a type becomes more readable.

**LB57.** The shared state was measured on an `Int`; closures running concurrently and accessing the same variable are outside this lesson.

## Trailing Closure Syntax

If the last parameter of a function is a closure, you can write the closure outside the parentheses. `perform(times: 2) { ... }` and `perform(times: 2, action: { ... })` are the same call. If the only argument of the function is the closure, the parentheses can drop altogether. This syntax only shortens the writing; when the closure runs does not change. The closure runs when the function calls it, and it runs as many times as the function calls it.

The block below writes a function: `perform` calls the given closure as many times as asked. The closure body writes to a `Probe`. The function is called first with two, then with three repetitions.

```swift
// Trailing.swift — trailing closure
func perform(times: Int, action: () -> Void) {
    for _ in 0..<times { action() }
}
let trailingProbe = Probe("trailing-closure")
trailingProbe.use { perform(times: 2) { _ = trailingProbe.eval(1) } }
trailingProbe.use { perform(times: 3) { _ = trailingProbe.eval(1) } }
print(trailingProbe.evaluations)
trailingProbe.report()
```

```
5
trailing-closure: 5 evaluations, 2 uses -> on-every-use
```

`perform(times: 2)` ran two times and `perform(times: 3)` three times; the total was five evaluations. Writing the closure outside the parentheses did not change the number of runs: the value of `times` decided how many times the closure would be called. On each call the body was evaluated again, and the moment reads `on-every-use`.

This syntax helps only with reading. When the closure is last among the parameters, the call looks like a control-flow statement: `perform(times: 3) { ... }` reads like a loop. But the rule behind it is the same: a closure is a value, and the function calls it as many times as it wants.

**LB58.** The trailing closure was measured with a closure that has no return value; the syntax of several trailing closures is outside this lesson.

## A Capture List Freezes the Value

When a closure captures a `var` variable from its surroundings it holds the variable itself; changes made to the variable later are reflected in the closure. If you want to change this, you write a capture list at the start of the closure: `{ [snapshot = count] in snapshot }`. The list takes a copy of the value of `count` at the moment the closure is created, and the closure now uses that. Even if `count` changes later, the number the closure sees stays the same.

The block below captures the same variable with two closures: one does not use a list, one does. After the closures are created the variable is changed and both are called. The value read passes through a `Probe`.

```swift
// CaptureList.swift — capture list
let listProbe = Probe("capture-list")
var count = 1
let live = { listProbe.eval(count) }
let frozen = { [snapshot = count] in listProbe.eval(snapshot) }
count = 5
print(listProbe.use { live() }, listProbe.use { frozen() })
listProbe.report()
```

```
5 1
capture-list: 2 evaluations, 2 uses -> on-every-use
```

The `live` closure gave `5`, the `frozen` closure `1`. Because `live` holds the variable itself, it saw the new value after the assignment `count = 5`. `frozen`, on the other hand, took a copy of the value of `count` at the moment it was created, under the name `snapshot`; changing the variable later did not reach it. Two calls brought two evaluations, and the moment is `on-every-use`.

The capture list is the way to write explicitly which value the closure will see. If you want a value to stay as it was when the closure was created, you write it in the list; if you want the closure to see the values the variable takes later, you do not use a list at all.

You can also write several items in a capture list; each item takes its own copy at the moment the closure is created.

**LB59.** The capture list was measured with an `Int` variable; capturing class instances weakly is outside this lesson and is covered in a later topic.

## The Limiting Measurement

If a closure uses a variable from its surroundings, it captures it. In capturing without a list, the closure is bound to the variable itself: if the variable changes later, the closure sees the new value on every call. A capture list is written inside square brackets and takes a value while the closure is being created: `{ [snapshot = level] in snapshot }`. The expression in the list is evaluated once while the closure is being built, and is not evaluated again at the calls. The measurement below builds two closures with the same variable, changes the variable and calls both of them twice.

```swift
// main.swift — capture list
let captureProbe = Probe("capture-list")

var level = 1
let plain = { level }
let listed = { [snapshot = captureProbe.eval(level)] in snapshot }
level = 5

print(captureProbe.use { plain() }, captureProbe.use { listed() })
print(captureProbe.use { plain() }, captureProbe.use { listed() })

let allProbes = [captureProbe]
for probe in allProbes { probe.report() }
tally(allProbes)
```

```
5 1
5 1
capture-list: 1 evaluations, 4 uses -> immediately
immediately: 1
on-first-use: 0
on-every-use: 0
never: 0
```

The `plain` closure saw 5 because it stayed bound to the `level` variable; the `listed` closure carried the value 1 it had when it was built. The expression in the capture list was evaluated once, while the closure was being built and before the four uses: the `capture-list` line gives `immediately`. The body running on every call and the capture list running once are two separate moments of the same closure.

**LB60.** `level` is a `var`, and the measurement shows only the capture of a value-type variable; capturing class instances was not measured in this lesson.

**LB61.** That a capture list runs once while the closure is built was measured with a single closure; that in code which builds the same closure in several places each build is a separate evaluation is outside this lesson.

## Summary

- A closure is unnamed behavior; `$0` and `$1` reach the arguments by position, and a single-expression body does not need `return`.
- If the last parameter is a closure, the closure can be written outside the parentheses (a trailing closure).
- A closure body runs on every call; the body of a closure that is never called never runs.
- A capture list is evaluated once while the closure is built; a later change to a variable captured without a list is visible at the call.
- The moment of this lesson: **on every use** (the closure body).

## Next Step

The examples in this topic used an expression like `values.min() ?? 0`: `min()` cannot return a value for an empty array. Swift carries this situation in the type as the absence of a value. The first lesson of the next topic, Optional Values, introduces this idea.
