Skip to content
Theory · Structs · lesson 2 of 9

Methods and mutating

Let's go back to the form Point. At the bottom it has two printed instructions: "how to write the point in parentheses" and "how to move the point right by a few steps". Each instruction works with the fields of the form it is printed on.

You could write such actions as separate functions that take a point as a parameter. But it is handier to keep an instruction next to the fields. You see at once what the form can do, and you don't have to search the whole program for it. Swift has methods for this:

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

    func describe() -> String {
        "(\(x), \(y))"
    }

    mutating func moveRight(by step: Int) {
        x += step
    }
}

var p = Point(x: 1, y: 2)
print(p.describe()) // (1, 2)

Let's walk through the program step by step:

  1. Inside struct Point, next to the properties, two methods are declared: describe and moveRight.
  2. var p = Point(x: 1, y: 2) creates an instance.
  3. p.describe() calls the method through a dot, on the specific instance p. The method builds a string from that instance's properties and returns (1, 2).

We'll take apart the word mutating in front of the second method a little further down.

A struct's method

A method of a struct is the same as one of an enum: a function inside the type's braces. You call it through a dot on an instance, p.describe(). Everything you know about functions works here too: parameters, argument labels, a result after ->.

The main difference from an ordinary function: a method sees the properties of its instance. Inside describe there are only the names x and y: these are the properties of the instance the method was called on. For p they are 1 and 2, and another point would have its own numbers.

The word self

Inside a method, self is the instance the method was called on. Writing x in a method's body is short for self.x.

Usually nobody writes self. You need it when a parameter has the same name as a property:

  • in a method with a parameter x, plain x means the parameter;
  • self.x in the same method means the property.

A method that changes a property

Here the instructions picture is inaccurate. On paper, any instruction lets you correct a field, while in Swift a method only reads properties by default. The method moveRight changes the property x, so its func has mutating in front of it. Let's call it:

SWIFT
p.moveRight(by: 9)
print(p.describe()) // (10, 2)

After the call p.x holds 10, and y has not changed.

If you remove mutating, the program does not compile:

TEXT
error: left side of mutating operator isn't mutable: 'self' is immutable

The compiler says the left side of += cannot change, because self is immutable. With a plain assignment x = x + step the message reads differently: cannot assign to property: 'self' is immutable. Both say the same thing: 'self' is immutable.

Why is that? Recall the parameters of a function: a parameter is a constant, and the body cannot change it. Inside a method, self is the same kind of constant. The word mutating allows the method to change self and works like inout. The method gets the instance, changes it, and on the way out the changes are written back into p.

A method without mutating reads properties but does not change them, like describe. That is why mutating goes only on methods that change properties.

Interviewers like to ask this: why does a struct's method need mutating, and why can't you call it on a let? The answer has two parts:

  • inside a method self is a constant, so a method that changes a property is marked mutating, and it gets self as an in-out parameter;
  • an instance in a let cannot change at all, so calling a mutating method on it does not compile.

Common mistake

A mutating method is called on an instance in a let:

SWIFT
let home = Point(x: 0, y: 0)
home.moveRight(by: 1) // ✗
TEXT
error: cannot use mutating member on immutable value: 'home' is a 'let' constant

The program does not compile, and not a single line runs. Swift does not quietly make a copy of home to move it: an instance in a let is a sealed form, and moveRight corrects a field on it. Like an inout parameter, a mutating method needs a variable. Declare the instance with var, and the call works.

Key points

  • A method is a function inside a type; you call it through a dot on an instance, and it sees that instance's properties.
  • self is the instance the method was called on; you write self.x when a parameter has the same name x.
  • A method that changes a property is marked mutating: without it, self inside the method is a constant.
  • A mutating method cannot be called on an instance in a let: such a program does not compile.