---
title: 'Structures and Classes'
source: 'https://academia.sh/en/courses/swift/structures-and-classes'
course: 'The Swift Language'
language: en
updated: '2026-10-21T05:03:13+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Measures that assigning a structure gives an independent copy and assigning a class gives a shared object'
    - 'Shows that a default value is evaluated per instance and only when none is given'
    - 'Measures that with copy-on-write the copy appears at the first mutation, not at assignment'
    - 'Shows that a single-owner value, or one shared inside a class, is never copied'
---

# Structures and Classes

Measuring copying on assignment for structures, sharing for classes, when a default value is built, and why a copy appears at the first mutation.

The previous lesson measured the ways of opening a box and listed when each one evaluates its expression. The value that comes out may be an `Int` or a `String`, but the real data of a program is usually a type that keeps several fields together: a point, an order, a document. Swift has two basic forms for such types (enumerations and actors are the subject of other lessons). A **structure** is a **value type**: its content is copied when it is assigned. A **class** is a **reference type**: assigning it gives a second name to the same object.

Most books explain this difference with the question "who sees what": does one name see the change made through the other? This lesson asks a different question: when does the copy appear? Does the assignment itself produce a copy, or does the copy appear only at the first mutation? The course's measuring axis splits this question into four moments: immediately, on first use, on every use, never. The `Probe` class is given below exactly as it is.

## The Core

Every lesson uses the same core. `Probe` counts how many times an expression is evaluated (`eval`) and how many times it is used (`use`); the `moment` property derives the moment from those two numbers. In the binding measurement of the opening lesson, the distribution over eight expressions was 2/1/3/2: two immediately, one on first use, three on every use, two never. In this lesson we measure only our own slice.

```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 first measurements below use three types. `Point` is a structure that holds two integers, and `Counter` is a class that holds a single counter. `Order` is a structure with a field that has a default value; the default value is a function call, and when the function runs it prints a line. This block has no output, because it only defines the types.

```swift
// Types.swift — a structure, a class and a structure with a default value
struct Point {
    var x: Int
    var y: Int
}

final class Counter {
    var value = 0
}

func makeNote() -> String {
    print("default value built")
    return "none"
}

struct Order {
    var id: Int
    var note = makeNote()
}
```

## Assignment: Copy or Share

We write the same two lines with a structure and with a class. With the structure, after `var second = first` and `second.x = 10`, `first` does not change. With the class, after `let right = left` and `right.value = 5`, the two names are bound to the same object and both see `5`.

```swift
// main.swift — a structure is copied on assignment, a class is shared
let first = Point(x: 1, y: 2)
var second = first
second.x = 10
print(first.x, second.x)

let left = Counter()
let right = left
right.value = 5
print(left.value, right.value)
```

```
1 10
5 5
```

The first line shows the structure: `first.x` stayed `1` and `second.x` became `10`. The assignment gave an independent copy. The second line shows the class: `left.value` and `right.value` are both `5`, because there is a single `Counter` object and two names are bound to it. Notice that `right` is a `let` constant and the `value` inside the object could still change: `let` forbids binding the name to another object, not changing the content of the bound object. With a structure, `let` means the whole value is immutable: a field of an immutable structure cannot be assigned, even if the field is declared with `var`.

The output separates the two types but does not say when the copy appears. The sentence "a structure is copied on assignment" exists in textbooks as a model; what we measure is observable behavior: after the change, the two values are independent. Whether the copy physically appears at assignment or later is measured below.

**TS9.** This measurement is for a structure with two integer fields and a class with a single field. Structures whose fields hold a reference to another class, and the copying of class fields inside a structure, are not part of this measurement.

## Passing to a Function

Assignment is only one face of the value and reference distinction; the same distinction shows up when an argument is passed to a function. When a function takes a structure as a parameter, it works with its own copy: a change made inside does not reach the caller's value. With a class parameter, the function and the caller look at the same object. In the measurement below, `moved` changes and returns a copy of the structure, while `bump` changes the counter in place.

```swift
// main.swift — a structure passed to a function is copied, a class is shared
func moved(_ point: Point) -> Point {
    var copy = point
    copy.x += 100
    return copy
}

func bump(_ counter: Counter) {
    counter.value += 100
}

let origin = Point(x: 1, y: 2)
let shifted = moved(origin)
print(origin.x, shifted.x)

let ticker = Counter()
bump(ticker)
print(ticker.value)
```

```
1 101
100
```

On the first line `origin.x` stayed `1` and `shifted.x` became `101`: `copy` inside `moved` was a separate value and the change was written only to it. On the second line `ticker.value` became `100`, because `bump` changed the same `Counter` object, and the function returns nothing. So the signature of a function whose parameter is a structure tells the reader "this function does not change the value I call it with", as long as the structure holds only values and the parameter is not `inout`. The signature of a function with a class parameter gives no such guarantee; you have to look at the body.

The practical result of this distinction shows in debugging. If a structure's value changed unexpectedly, the change was made in the scope of that value itself; if a class object changed unexpectedly, every place that can reach the object is a suspect. Using value types between function signatures narrows the field of suspects.

**TS10.** This measurement is for one structure parameter and one class parameter. Passing with an `inout` parameter, and a structure captured by a closure, are not part of this measurement.

## The Default Value: Once per Instance

The `note` field of the `Order` structure starts with the result of the `makeNote()` function. This default value is not built once and shared among all instances; every `Order` instance builds its own default value. Moreover, the value is not computed at all if the caller supplies the field itself. We measure with three orders: two with the default note and one with an explicit note.

```swift
// main.swift — the default is evaluated per instance, but only when not given
let o1 = Order(id: 1)
let o2 = Order(id: 2)
let o3 = Order(id: 3, note: "urgent")
print(o1.note, o2.note, o3.note)
```

```
default value built
default value built
none none urgent
```

`makeNote()` ran twice, for two of the three orders. For the third order, which gave the note explicitly, the function was never called. Both places where it ran appear before the print line; that is, the default value was computed on the calling line, while the instance was being built.

One result follows: every expression written as a default value is evaluated as many times as there are instances, minus the number of supplied values. If an expensive expression is written as a default value, that cost is paid on every instance. The Properties lesson measures deferring this cost with `lazy`.

**TS11.** This measurement was made with three instances and one field with a default value. The evaluation order of several fields with default values, and the same case in classes, were not measured separately.

## Copy-on-Write

When a large value type is assigned, copying all of it for real would be wasteful. Some types of the standard library therefore use a technique called **copy-on-write**: assignment only adds a second reference to a storage, and the copy appears only when the shared storage has to be changed, at the first mutation. We can build the same technique by hand in our own type. The `Document` structure keeps its text in a `Storage` class. `append` first asks with `isKnownUniquelyReferenced` whether we are the only owner of the storage; if not, it copies the storage and reports the moment of copying through the `onCopy` closure.

```swift
// Document.swift — a structure boxed in a class, copying on write
final class Storage {
    var text: String

    init(text: String) {
        self.text = text
    }
}

struct Document {
    private var storage: Storage
    private let onCopy: () -> Void

    init(text: String, onCopy: @escaping () -> Void) {
        storage = Storage(text: text)
        self.onCopy = onCopy
    }

    var text: String {
        storage.text
    }

    mutating func append(_ more: String) {
        if !isKnownUniquelyReferenced(&storage) {
            onCopy()
            storage = Storage(text: storage.text)
        }
        storage.text += more
    }
}

final class Holder {
    var document: Document

    init(_ document: Document) {
        self.document = document
    }
}
```

Look at the code closely. The `storage` field is `private`, so it is invisible from outside; only the read-only `text` property is exposed. Changes pass only through `append`, which is marked `mutating`, and the decision to copy is made only there. This narrowing matters: because the copying rule stays in one place, no other method of the type can change the storage while it is shared. The `onCopy` closure is there for the measurement; production code would not contain such a notification, but here it lets us count in exactly which call the copy appears. The `Holder` class will be used in the last measurement: it makes a `Document` value the field of a class object.

This block has no output; it only defines `Document` and the `Holder` that wraps it in a class. Now we make the assignment `duplicate = original`, change both, and measure exactly when the copy appears. The `onCopy` closure is wrapped in `cow.eval`; `append` is called twice.

```swift
// main.swift — the copy appears at the first mutation, not at assignment
let cow = Probe("copy-on-write")
let original = Document(text: "ab", onCopy: { _ = cow.eval(()) })
var duplicate = original

_ = cow.use { duplicate.append("c") }
_ = cow.use { duplicate.append("d") }
cow.report()
print(original.text, duplicate.text)
```

```
copy-on-write: 1 evaluations, 2 uses -> on-first-use
ab abcd
```

The assignment itself produced no copy: the evaluation count is one and the moment is **on first use**. The first `append` was sharing the storage with another value, so the copy appeared at that moment. The second `append` was the sole owner of the copied storage, so no copy was needed. The last line confirms the result: `original` is still `ab`, while `duplicate` is `abcd`. There are two values, but only one copy took place, and it took place not at assignment but at the first mutation.

We also write out what the numbers mean. One evaluation says the copy appeared once; two uses say two `append` calls were made. If the evaluation count were equal to the use count, every change would give birth to a copy and the moment would be **on every use**. If it were zero, no change would have needed a copy and the moment would be **never**. The measured one is below two and it appeared at the first use; these three numbers together decide the moment.

This measurement answers the question we asked at the start. In a value type like `Document`, which shares its storage, the copy appears after assignment, during the first write. For the reading side there is no difference; the cost of the copy falls on the first writer. The choice between a value type and a reference type can therefore be read not only as a semantic preference but also as a question of when the cost is paid.

**TS12.** This measurement is only for the `Document` type we built by hand; the copy-on-write behavior of the standard library's array, dictionary and string types was not measured in this lesson.

## The Bounding Measurement

The rule we built for copy-on-write is this: the copy appears at the first mutation. The rule has a condition, the storage must be shared with another owner. This condition does not hold in two cases. In the first, nobody has become a co-owner of the `solo` document; in the second, the document is inside a class and two names are bound to the same class object, that is, there is no second `Document` value sharing the storage. In both, `append` is called twice.

```swift
// main.swift — a single-owner document, or one shared inside a class, is not copied
let unique = Probe("single-owner")
var solo = Document(text: "ab", onCopy: { _ = unique.eval(()) })
_ = unique.use { solo.append("c") }
_ = unique.use { solo.append("d") }

let shared = Probe("class-sharing")
let holder = Holder(Document(text: "ab", onCopy: { _ = shared.eval(()) }))
let alias = holder
_ = shared.use { holder.document.append("c") }
_ = shared.use { alias.document.append("d") }

unique.report()
shared.report()
print(solo.text, holder.document.text, alias.document.text)
```

```
single-owner: 0 evaluations, 2 uses -> never
class-sharing: 0 evaluations, 2 uses -> never
abcd abcd abcd
```

In both cases `onCopy` was never called: the moment is **never**. The single-owner document was not copied even at the first mutation, because there was no second owner to share with. The document inside the class was not copied either: since `holder` and `alias` are bound to the same `Holder` object, an append made through one name is visible through the other, and a second copy of the `Document` value never appears. The last line confirms this sharing: all three names print `abcd`.

This limit narrows the lesson's rule. The sentence "a value type is copied on assignment" and the sentence that completes it, "the copy appears at the first mutation", hold only if there are two separate values sharing the storage. If a value type is the field of a class, the reference semantics of the class wins: no copy appears, because the class object is already single.

**TS13.** This measurement is for a single class object and two names. The case where the class object itself, together with its `Document` field, is assigned to another value, or where two different `Holder` objects carry the same document, was not measured.

## Summary

- A structure gives an independent copy on assignment; when a class is assigned, two names are bound to the same object.
- A class name declared with `let` does not make the object immutable; a structure declared with `let` makes the whole value immutable.
- A default value is evaluated once per instance, and never if the caller supplies the field.
- With copy-on-write the copy appears not at assignment but at the first mutation, and only if the storage is shared.
- With a single-owner value, or one shared inside a class, the copy never appears.
- This lesson's moment: **on first use**. In a copy-on-write document the copy appears at the first mutation.

## Next Step

In this lesson we measured whether a type is a value or a reference and when the copy appears. Some types are not a list of fields but a choice among several distinct states: a measurement is either in degrees or a label. The next lesson measures enumerations, which describe those states and the values carried along with each of them.
