I am curious why this doesn't work:
public protocol MyProtocol {
var i: Int { get set }
}
public protocol MyProtocol2: class, MyProtocol {}
public extension MyProtocol2 where Self: AnyObject {
func a() {
i = 0 <-- error
}
}
Error:
Cannot assign to property: 'self' is immutable
Why? Only classes can adopt MyProtocol2. If I add : class
declaration behind MyProtocol it works. I do not understand why it doesn't work on a subprotocol.
Your example doesn't compile because MyProtocol
isn't class-bound, and as such can have mutating
requirements and extension members. This includes property setters, which are by default mutating
. Such members are free to re-assign a completely new value to self
, meaning that the compiler needs to ensure they're called on mutable variables.
For example, consider:
public protocol MyProtocol {
init()
var i: Int { get set } // implicitly `{ get mutating set }`
}
extension MyProtocol {
var i: Int {
get { return 0 }
// implicitly `mutating set`
set { self = type(of: self).init() } // assign a completely new instance to `self`.
}
}
public protocol MyProtocol2 : class, MyProtocol {}
public extension MyProtocol2 where Self : AnyObject {
func a() {
i = 0 // error: Cannot assign to property: 'self' is immutable
}
}
final class C : MyProtocol2 {
init() {}
}
let c = C()
c.a()
If this were legal, calling c.a()
would re-assign a completely new instance of C
to the variable c
. But c
is immutable, therefore the code is not well formed.
Making MyProtocol
class bound (i.e protocol MyProtocol : AnyObject
or the deprecated spelling protocol MyProtocol : class
) works because now the compiler knows that only classes can conform to MyProtocol
. Therefore it imposes reference semantics by forbidding mutating
requirements and extension members and therefore prevents any mutations of self
.
Another option at your disposal is to mark the setter for the requirement i
as being nonmutating
– therefore meaning that it can only be satisfied by a non-mutating setter. This makes your code once again well-formed:
public protocol MyProtocol {
init()
var i: Int { get nonmutating set }
}
public protocol MyProtocol2 : class, MyProtocol {}
public extension MyProtocol2 where Self : AnyObject {
func a() {
i = 0 // legal
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With