Swift Bite - Mutating Functions

Value types such as Structs and Enums can have methods in Swift. These methods may want to modify the properties defined in the types. Consider an example of a struct Stack that has an array property named items and a function push(item:)


struct Stack {
    private var items = [Int]()
    func push(item: Int) {
        // Error - Cannot use mutating member 
        // on immutable value: 'self' is immutable
        items.append(item) 
    }
}

var stack = Stack()
stack.push(item: 10)

Here, the push(item:) function attempts to add the item to the items array. This will throw a compile-time error as we are trying to change the instance property, items, from within the struct. It is not possible to change the properties inherently because, by default, the self is declared as a let for the value types.

Let’s understand this further. self is inherently passed as a function parameter to every instance method of the type. As self is declared as let by default, there is no way we can mutate the instance (self) or any properties on that instance from within the function.

As an alternative approach, we can very well create a new instance of the Stack, copy the existing items array and add the new item to the items array.

struct Stack {
    var items: [Int] = Array()
    func push(item: Int) -> Stack {
        var copy = items
        copy.append(item)
        return Stack(items: copy)
    }
}

let stack = Stack()
let newStack = stack.push(item: 10)

This ends up creating a new copy of the struct every time we try to push a new item in the Stack.

mutating Keyword

Swift provides a better way to mutate the value type and its properties inherently. Mark a function as mutating. This makes self passed as an inout parameter to the function and lets us change its or its property’s value provided the properties are declared as a var. Here is the code —

struct Stack {
    private var items: [Int] = Array()
    mutating func push(item: Int) {
        // Works fine! self is passed as 
        // an inout variable here
        self.items.append(item) 
    }
}
var stack = Stack()
stack.push(item: 10)

A function defined in the value type must be marked as mutating if the instance or the properties of the value type need to be mutated.