I often find myself liking how, in the initializers for struct
s, enum
s, and protocol
s, I can write something like self = someValue
. This is great when I have some predefined values or am cloning an existing value.
However, this syntax doesn't work for class
es. I can't really figure out why, either.
Cannot assign to value: 'self' is immutable
If the concern is double-initialization, the Swift compiler knows if, when, and where I call designated super
or self
initializers, so it knows whether I'm done initializing this instance.
If the concern is that I haven't yet called a designated initializer, then it should be fine because I'd just be making this instance a reference to the other one (2 vars 1 pointer).
If the concern is that concurrent access might have caused self
to already be initialized... well that's nonsense because we're in the initializer and Swift initializers are not susceptible to that.
And after all that I discovered I can get around this with a single-use protocol:
class MyClass {
let content: String
init(content: String) {
self.content = content
}
convenience init(from1 other: MyClass) {
self = other // Cannot assign to value: 'self' is immutable
}
}
protocol MyProto {}
extension MyClass: MyProto {}
extension MyProto {
init(from2 other: Self) {
self = other
}
}
let foo = MyClass(content: "Foo")
print(MyClass(from1: foo)) // Never would've compiled in the first place
print(MyClass(from2: foo)) // Perfectly OK!
So why is this denied in common usage, but allowed in protocol-extensions?
It seems this currently behaves as expected.
The whole problem has been discussed on the swift forums: Assigning to Self in Protocol Extensions
The last time this quirk came up in internal discussions, the thought some of us had was that it might be worthwhile to prohibit classes from conforming to protocols with mutating requirements altogether. If you think about it, this makes some amount of sense — it seems like it would be quite hard to write code that can operate on both mutable values and mutable references generically, since the latter do not have value semantics:
var x = y x.mutatingProtocolRequirement() // did y change too?
However the discussion sort of fizzled out.
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