Swift mutating vs nonmutating keyword
Swift is a programming language developed by Apple, designed to be safe, fast, and modern. One of its key features is the use of value types, which restrict the way state can be shared across API boundaries. In this article, we’ll explore how value types work in Swift, and how they limit mutations to local copies of values.
mutating
In Swift, all basic types such as integers, floating-point numbers, and Booleans are value types. Structs and enums are also value types, while classes are reference types. When we pass a value type as an argument to a function or method, a copy of the value is created. This means that mutations made to the copy do not affect the original value. This is in contrast to reference types, where mutations made to an instance can affect other references to the same instance.
To perform mutations on a value type, we need to mark the function or method as “mutating”. This lets Swift know that the function may change the properties of the value it’s working with, and allows it to assign a new value to the “self” parameter. For example:
struct Point {
var x: Int
var y: Int
mutating func moveBy(x: Int, y: Int) {
self.x += x
self.y += y
}
}
var point = Point(x: 0, y: 0)
point.moveBy(x: 10, y: 20)
print(point) // Prints "Point(x: 10, y: 20)"
In this example, we define a “Point” struct with two properties, “x” and “y”. We then define a “moveBy” method that takes two parameters and adds them to the point’s “x” and “y” properties. Note the “mutating” keyword before the method name, indicating that it changes the point instance.
We can also define mutating protocol requirements, which allow us to specify that a conforming type must implement a mutating method. For example:
protocol Vehicle {
var speed: Double { get set }
mutating func accelerate(by speed: Double)
}
struct Car: Vehicle {
var speed = 0.0
mutating func accelerate(by speed: Double) {
self.speed += speed
}
}
In this example, we define a “Vehicle” protocol with a “speed” property and a “mutating” “accelerate” method. We then define a “Car” struct that conforms to the “Vehicle” protocol and implements the “accelerate” method to increase its speed.
nonmutating
Swift also allows us to mark certain contexts as “nonmutating”, which indicates that a property or method does not modify the value it’s working with. This is less common than marking functions as “mutating”, but can be useful in certain situations. For example:
struct Circle {
var radius: Double
var diameter: Double {
nonmutating get {
return radius * 2
}
set {
radius = newValue / 2
}
}
}
In this example, we define a “Circle” struct with a “radius” property and a “diameter” computed property. Note the “nonmutating” keyword before the “get” keyword in the “diameter” property, indicating that it does not modify the circle instance. The “set” method, on the other hand, is marked as “mutating” because it changes the “radius” property.
Conclusion
In conclusion, Swift’s use of value types allows for safe and efficient code, with mutations restricted to local copies of values. By marking functions and methods as “mutating”, we can perform mutations on value types, and by marking properties and