Value semantics in classes

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?

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.

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!