Why are mutating methods disallowed on classes?
Ordinarily, when we need to modify a class instance we can just perform the changes we want. And if we often perform the same sequence of modifications, we can encapsulate them into an instance method.
However, when we need to assign a new value to the variable itself, we can still do so directly, but not in an instance method.
If we need to encapsulate some behavior which includes reassigning self
, we can do so easily on a struct
by creating a mutating
method, but we have to jump through hoops to do the same thing on a class.
We can, of course, write a static or global function taking a class instance inout
, and assign to it there. We can even conform the class to a protocol with a mutating
extension method. But we can’t create a mutating
instance method on the class itself.
• • •
For example, let’s say we have a class A
and its subclass B
. We can assign a value of type B
to a variable of type A
, because of the subclass relationship. Now suppose we have some logic whereby we need to perform such an assignment. That is, a variable of type A
needs to be assigned a value of type B
.
There may be additional logic involved, so we wish to encapsulate it into a method. But the obvious approach doesn’t work:
class A {}
class B: A {}
extension A {
// error: 'mutating' isn't valid on methods in classes or class-bound protocols
mutating func becomeB() {
// error: cannot assign to value: 'self' is immutable
self = B()
}
}
If we remove mutating
then the second error remains. We cannot reassign self
in an instance method on A
.
• • •
The behavior we want is still possible to achieve, but it’s more complicated and non-obvious:
protocol P {}
extension A: P {}
extension P where Self == A {
mutating func becomeB() {
self = B()
}
}
This works, and it lets us do things like:
var a = A()
type(of: a) // A
a.becomeB()
type(of: a) // B
• • •
So we can, in fact, achieve a mutating method on a class instance, but instead of writing our logic directly in a function defined on the class, we had to create an empty protocol, conform the class to it, and place the mutating method in a same-type-constrained extension.
Why can’t we just create mutating
methods on classes to begin with?