5

I often find myself liking how, in the initializers for structs, enums, and protocols, 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 classes. 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?

pkamb
  • 33,281
  • 23
  • 160
  • 191
Ky -
  • 30,724
  • 51
  • 192
  • 308
  • 2
    Does it still work if the protocol is class-constrained? – jscs Jan 18 '19 at 22:32
  • 2
    @JoshCaswell I just tested it and it doesn't. It seems to me this is allowed because they want to allow this for value types and there is no way how to that *only* for value types. – Sulthan Jan 18 '19 at 22:48
  • https://bugs.swift.org/browse/SR-5255 Discussion is somehow related to this. – Sulthan Jan 18 '19 at 22:51
  • 1
    BTW, _"that's nonsense because we're in the initializer and Swift initializers are not susceptible to that"_: no, as I commented there it's not `init` that's safe, it's the assignment to the `static` property. – jscs Jan 18 '19 at 23:25
  • @JoshCaswell Oh... I didn't catch that nuance. Could you clarify with an answer there? – Ky - Jan 19 '19 at 00:26

2 Answers2

3

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.

Ky -
  • 30,724
  • 51
  • 192
  • 308
Sulthan
  • 128,090
  • 22
  • 218
  • 270
0

There is a pitch for this functionality on the Swift Evolution forum:

Allow self = x in class convenience initializers

https://forums.swift.org/t/allow-self-x-in-class-convenience-initializers/15924

The pitch mentions how this syntax already works for struct value types, and in protocol initializers as mentioned in this question:

Swift's own standard library and Foundation overlay hack around this missing functionality by making classes conform to dummy protocols and using protocol extension initializers where necessary to implement this functionality.

The proposal seems to have widespread support, including from Chris Lattner:

+1, long overdue!!

However the pitch has not yet been implemented. Once implemented, it can be reviewed and potentially added to the Swift language.

pkamb
  • 33,281
  • 23
  • 160
  • 191