Here's the code:
protocol A {
var a: Int { get set }
}
extension A {
var convenientAccessor: Int {
get { return a }
set { a = newValue }
}
}
class B: A {
var a: Int = 0
}
func acceptsB (instance: B) {
instance.a = 1 // This compiles
instance.convenientAccessor = 2 // This does not
}
I sort of understand the problem here, but I really would love both an answer from someone who understands it more deeply, and more importantly a workaround for my problem which is that I want to pass around known class types and be able to use convenient accessors without being inhibited by the non-possibility that I'm using a value type. In my case, the protocols which define these convenient accessors should not be class-bound (they are perfectly valid and useful for value types), so while that is technically a workaround, it is not satisfactory to me.
Let's start with a summary:
convenientAccessor
is a writable property, thus any assignment on it will result in a mutation. And this mutation happens inside your function.The thing is that when the
convenientAccessor
property is used the compiler treatsinstance
as protocolA
, and because arguments to a function arelet
's, it doesn't allow mutations on them, as at this point the protocol can be implemented by a value type also.Two solutions that come into my mind are:
restrict protocol
A
to only classes:This informs the compiler that only classes can conform to the protocol, which allows the mutation on any kind of variables (
let
orvar
)make the function parameter
inout
:This creates a "write hole" as now
instance
is writable, so any changes to it are propagated back upstream, even ifB
is a value type.Personally, I'd go with #1 if you plan in using only classes, as you mentioned in the question.