Skip to content
Theory · Structs · lesson 1 of 9

Structs

What you'll learn

What you'll learn:

  • describe your own type with a struct: properties, default values, creating an instance;
  • write methods and explain why a method needs the word mutating;
  • add computed properties, the willSet and didSet observers, your own init and static;
  • explain why changing a copy of a struct leaves the original alone.
A form Point with the field x: 1 and its photocopy where the field reads x: 10, while the original is unchanged

Picture a paper form. The header reads Point, and below it are two fields with captions: x and y. A blank form is a template: it says which fields there are and what goes into them. A filled-in form holds data: the field x reads 1, and the field y reads 2.

You can photocopy the form. On the copy, someone crosses out the field x and writes 10, while the original still reads x: 1. Forms filled in from one template each live a life of their own.

Why would a program need such a form? A point on a plane is two numbers, and you could keep them in two variables, x and y. But with ten points you would have twenty variables, and you would have to keep track of which y belongs to which x. It is handier to collect both numbers under one name. That is what structs are for in Swift:

SWIFT
struct Point {
    var x: Int
    var y: Int
}

let start = Point(x: 1, y: 2)
print(start.x) // 1
print(start.y) // 2
print(start) // Point(x: 1, y: 2)

Let's walk through the program step by step:

  1. The lines from struct to the closing brace describe the template of the form Point. On their own they create nothing.
  2. Point(x: 1, y: 2) fills in a new form: it writes 1 into the field x and 2 into the field y. The finished form goes into the constant start.
  3. start.x and start.y read the fields through a dot, and the program prints 1 and 2.
  4. print(start) prints the whole form: the type name and every field with its value.

Declaring a struct

A struct is your own type that collects several values under one name. The declaration struct Point has three parts:

  • struct is the keyword that starts the declaration;
  • Point is the name of the new type; type names start with a capital letter, like Int and String;
  • the braces hold the fields of the form, each with its own name and type.

The fields of a struct are its properties: as with enums, you read them with a dot and no parentheses. But a struct's property holds a value, and it is declared like an ordinary variable or constant: var x: Int.

You use the new type the same way as Int or String. You can declare a constant of type Point, pass a point to a function or put several points into an array [Point].

Creating an instance

A specific value of type Point, a filled-in form, is called an instance of the struct. To create an instance, you write the type name and, in parentheses, the values of all the properties with labels, like the arguments of a function call. Swift gives a struct this way of creating values on its own: you write nothing for it.

Its rules are these:

  • the labels match the property names: x: and y:;
  • the order is the same as in the declaration: Point(y: 2, x: 1) does not compile, with the error argument 'x' must precede argument 'y';
  • every property needs a value: Point(x: 1) gives the error missing argument for parameter 'y' in call.

Here the form picture is inaccurate. You can hand in a paper form with a blank field, but an instance of a struct always has all its properties filled in. Swift will not create a Point until it knows both x and y.

Default values

Sometimes a form already has a value printed in, and you change it only when you need to. In a struct, such a value is written right in the property's declaration:

SWIFT
struct Alarm {
    var hour = 7
    var minute = 30
}

let usual = Alarm()
let early = Alarm(hour: 6)
print(usual) // Alarm(hour: 7, minute: 30)
print(early) // Alarm(hour: 6, minute: 30)

A property with a default value can be left out when you create an instance:

  • Alarm() takes both default values: 7 hours and 30 minutes;
  • Alarm(hour: 6) sets the hour and leaves the minutes at their default.

The property types are not written here: Swift infers them from the values, as for an ordinary variable.

Reading and changing through a dot

You read a property through a dot: start.x. You change it through a dot too, if the instance is stored in a var:

SWIFT
var finish = Point(x: 1, y: 2)
finish.x = 10
print(finish) // Point(x: 10, y: 2)

finish.x = 10 corrects one field, and y stays as it was.

You can change a property only if two declarations both allow it:

  • the instance is declared with var: in an instance stored in a let, no property can be rewritten;
  • the property itself is declared with var: a let property does not change even in an instance stored in a var.

Common mistake

A property is changed on an instance declared with let:

SWIFT
start.x = 10 // ✗
TEXT
error: cannot assign to property: 'start' is a 'let' constant

The program does not compile. The constant start is a sealed form: you can change neither the whole form nor single fields, even though x in Point is declared with var. If the form needs corrections, declare the instance with var, like finish.

Key points

  • A struct collects several values under one name: struct is followed by the type name and the properties in braces.
  • An instance is created with labels for all the properties in order: Point(x: 1, y: 2); a property with a default value can be left out.
  • A property is read and changed through a dot: finish.x = 10.
  • An instance in a let does not change at all: none of its properties can be rewritten.