为什么 Swift 在 class init 中不允许对 self 赋值,但在协议 init 中却不允许?
Why does Swift disallow assignment to self in class init, but not in protocol init?
我经常发现自己喜欢在 struct
s、enum
s 和 protocol
s 的初始化程序中,我可以编写类似 self = someValue
的内容。当我有一些预定义值或正在克隆现有值时,这非常有用。
但是,此语法不适用于 class
es。我也想不通为什么。
Cannot assign to value: 'self' is immutable
如果问题是双重初始化,Swift 编译器知道我是否、何时以及在何处调用指定的 super
或 self
初始化程序,因此它知道我是否完成初始化此实例。
如果担心的是我还没有调用指定的初始值设定项,那么它应该没问题,因为我只是让这个实例成为对另一个实例的引用(2 个变量 1 个指针)。
如果担心的是并发访问可能导致 self
已经被初始化……那是无稽之谈,因为我们在初始化器中并且 Swift initializers are not susceptible to that.
毕竟我发现我可以使用一次性协议解决这个问题:
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!
那么为什么这在常见用法中被拒绝,而在协议扩展中却被允许?
目前这似乎符合预期。
整个问题已在 swift 论坛上讨论过: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.
在 Swift Evolution 论坛上有一个关于此功能的宣传:
Allow self = x
in class convenience initializers
https://forums.swift.org/t/allow-self-x-in-class-convenience-initializers/15924
演讲中提到了这种语法如何适用于结构值类型,以及在这个问题中提到的协议初始值设定项中:
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.
该提议似乎得到了广泛的支持,包括来自 Chris Lattner 的支持:
+1, long overdue!!
但是该音调尚未实施。实施后,可以对其进行审查并可能将其添加到 Swift 语言中。
我经常发现自己喜欢在 struct
s、enum
s 和 protocol
s 的初始化程序中,我可以编写类似 self = someValue
的内容。当我有一些预定义值或正在克隆现有值时,这非常有用。
但是,此语法不适用于 class
es。我也想不通为什么。
Cannot assign to value: 'self' is immutable
如果问题是双重初始化,Swift 编译器知道我是否、何时以及在何处调用指定的 super
或 self
初始化程序,因此它知道我是否完成初始化此实例。
如果担心的是我还没有调用指定的初始值设定项,那么它应该没问题,因为我只是让这个实例成为对另一个实例的引用(2 个变量 1 个指针)。
如果担心的是并发访问可能导致 self
已经被初始化……那是无稽之谈,因为我们在初始化器中并且 Swift initializers are not susceptible to that.
毕竟我发现我可以使用一次性协议解决这个问题:
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!
那么为什么这在常见用法中被拒绝,而在协议扩展中却被允许?
目前这似乎符合预期。
整个问题已在 swift 论坛上讨论过: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.
在 Swift Evolution 论坛上有一个关于此功能的宣传:
Allow
self = x
in class convenience initializershttps://forums.swift.org/t/allow-self-x-in-class-convenience-initializers/15924
演讲中提到了这种语法如何适用于结构值类型,以及在这个问题中提到的协议初始值设定项中:
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.
该提议似乎得到了广泛的支持,包括来自 Chris Lattner 的支持:
+1, long overdue!!
但是该音调尚未实施。实施后,可以对其进行审查并可能将其添加到 Swift 语言中。