As far as I can see, Swift seems to actively prevent us from reassigning self in classes:
class C {
func reassignSelf(to other: C) {
self = other // ERROR: Cannot assign to value: 'self' is immutable
}
}
and
class C {
mutating func reassignSelf(to other: C) { // ERROR: 'mutating' isn't valid on methods in classes or class-bound protocols
self = other // ERROR: Cannot assign to value: 'self' is immutable
}
}
So I'm assuming that we are not allowed to reassign self in classes (please correct me if I'm wrong).
But:
It is possible to reassign self in a class via a protocol's default implementation:
protocol P {
mutating func reassignSelf(to other: Self)
}
extension P {
mutating func reassignSelf(to other: Self) { self = other }
}
class C : P {
var v: Int
var addressStr: String { "\(Unmanaged.passUnretained(self).toOpaque())" }
init(v: Int) { self.v = v }
}
func test() {
let a = C(v: 1)
var b = C(v: 2)
let c = b
print(a.v, a.addressStr) // 1 0x0000000100703c70
print(b.v, b.addressStr) // 2 0x0000000100703c90
print(c.v, c.addressStr) // 2 0x0000000100703c90
print(a === b) // false
print(a === c) // false
print(b === c) // true
b.reassignSelf(to: a)
print(a.v, a.addressStr) // 1 0x0000000100703c70
print(b.v, b.addressStr) // 1 0x0000000100703c70
print(c.v, c.addressStr) // 2 0x0000000100703c90
print(a === b) // true <-- These are now referencing the same instance.
print(a === c) // false
print(b === c) // false
}
test()
So, while Swift disallows reassigning self in classes, it allows us to do just that (I guess even accidentally) via a default implementation of some protocol ...
It would be interesting to learn more about the rationale behind this seemingly inconsistent behavior, that is:
Why is it important to prevent reassigning self in classes?
Why is it not prevented to (even accidentally) do so via a protocol's default implementation?
Thanks for providing those links! I've read them through and they are mostly about the problem as formulated in the forked thread, and while they provide answers to the problem from that perspective, they don't quite answer the specific questions of this thread, namely:
Given the program in the OP (which demonstrates that it is possible to reassign self in classes, and that the way it is done is such that it might happen accidentally):
I've quoted the parts of the linked discussions that comes closest to answering these questions here:
So what I'm saying is that after reading and understanding the above (and the other parts of the linked discussions) I've yet to see the rationale for the seemingly inconsistent behavior demonstrated by the program in the OP. That is: Why does Swift allow a default implementation of a protocol to reassign self of a class from under our feet (while actively preventing us from doing it explicitly within the class)?
But the effect is the same (see demonstration program in OP) no matter if the reassignment is done via a default protocol implementation of a method, or if it had been allowed in a method on the class.
And unless I'm misunderstanding the following:
The answer from @jrose seems to suggest that assigning self of a class (no matter how) is something that is meant to be left out of the language.
But it's not left out of the language (as shown by the demonstration program). And I'd like to know if it is
Expected behavior, working as designed (and if so, why allowing this indirect form that can happen accidentally via a default implementation of a protocol, while disallowing the explicit form in the class)?
An unfortunate by-product that can't be helped
A bug (ie it shouldn't be allowed, because it's meant to be "left out of the language to avoid introducing a point of confusion").
Not really, what if mutating in class reassign all references pointing to it (which frankly, is more or less the same as non-mutating function, but there will be people thinking like that for sure).
Alternatives would be:
Disallow P.reassignSelf specifically for class types.
Disallow any class types to conform to P.
None seems to be obviously better choice.
Probably need the core team to answer that. Anyhow, it’s a good thought exercise.