I'm curious why do mutating properties or methods defined in a protocol extension have value semantics when the protocol is implemented by a class.
Let's say we have a protocol and a mutating member implemented in an extension:
protocol P {}
extension P {
var p: Int {
get {
return 5
}
set {
_ = newValue
}
}
}
If we make a class that implements the protocol and try to modify p of a class instance, we get the compiler error:
class C: P {}
let c = C()
c.p = 10 // error: cannot assign to property: 'c' is a 'let' constant
Is this just a limitation in the current Swift compiler or the behaviour is expected and why?
bzamayo
(Benjamin Mayo)
2
I'm not sure if this limitation could be lifted in cases where the compiler can locally prove the instance is a class, but at least right now you need to indicate your protocol is class based.
Declare protocol P : AnyObject {} and you can assign with reference semantics.
I'm aware that declaring a protocol like P : AnyObject or P : class will yield the expected behaviour, but I would also expect the reference semantics even without doing that when the instance is known to be of a reference type.
Joe_Groff
(Joe Groff)
4
From the extension's point of view, it doesn't know whether it's getting applied to a class or not. If you define a mutating operation in the extension, it could wholesale replace the reference:
extension P {
mutating func swap(with x: inout Self) {
let tmp = x
self = tmp
x = tmp
}
}
and since we can't rule out that possibility from the call site, the reference you invoke the method on must be mutable.
7 Likes
Ah, that makes perfect sense. Thanks for the explanation!