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:
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:
- Inside
struct Point, next to the properties, two methods are declared:describeandmoveRight. var p = Point(x: 1, y: 2)creates an instance.p.describe()calls the method through a dot, on the specific instancep. 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, plainxmeans the parameter; self.xin 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:
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:
error: left side of mutating operator isn't mutable: 'self' is immutableThe 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
selfis a constant, so a method that changes a property is markedmutating, and it getsselfas an in-out parameter; - an instance in a
letcannot change at all, so calling amutatingmethod on it does not compile.
Common mistake
A mutating method is called on an instance in a let:
let home = Point(x: 0, y: 0)
home.moveRight(by: 1) // ✗error: cannot use mutating member on immutable value: 'home' is a 'let' constantThe 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.
selfis the instance the method was called on; you writeself.xwhen a parameter has the same namex.- A method that changes a property is marked
mutating: without it,selfinside the method is a constant. - A
mutatingmethod cannot be called on an instance in alet: such a program does not compile.