[swift-evolution] Assigning to 'self' in protocol extensions
rintaro ishizaki
fs.output at gmail.com
Fri Jan 20 00:52:11 CST 2017
Hi all,
I just want to raise an issue.
I currently don't have any concrete ideas about it, but I believe we should
discuss this before ABI stabilization.
Default implementation for mutating methods
Swift protocol extension allows assigning to self in default
implementations for mutating methods:
protocol Resettable {
mutating func reset()
init()
}
extension Resettable {
mutating func reset() {
self = Self() // Assigning to 'self'.
}
}
And you can:
class Foo : Resettable {
var value: Int
required init() {
value = 0
}
}
In this example, Foo class conforms to Resettable, by the default
implementation of mutating func reset(). However, this can cause an
unexpected behavior for classes:
var ref1 = Foo()
let ref2 = ref1
assert(ObjectIdentifier(ref1) == ObjectIdentifier(ref2))
ref1.value = 42
assert(ObjectIdentifier(ref1) == ObjectIdentifier(ref2))
ref1.reset()
assert(ObjectIdentifier(ref1) != ObjectIdentifier(ref2))
assert(ref1.value == 0)
assert(ref2.value == 42)
>From the perspective of the caller, I think, this behavior is
counterintuitive because we use "reference types" with an expectation: the
referencing address would never be changed *unless* we explicitly replace
the object by re-assigning to the variable in call sites, e.g.,
var ref: Foo = Foo()
ref = Foo()
<https://gist.github.com/rintaro/e9d606e2a6d833a043cf43a9a3e14670#default-implementation-for-initializers>Default
implementation for initializers
Similar to methods, initializers also have this issue:
protocol HasDefault {
static var _default: Self { get }
init()
}
extension HasDefault {
init() {
self = Self._default // Here it is.
}
}
final class Foo : HasDefault {
let value: Int
init(value: Int) {
self.value = value
}
static var _default = Foo(value: 0)
}
let obj = Foo()
assert(obj.value == 0)
This behavior allows us to implement a kind of "factory initializer".
protocol Factory {
init(factory: () -> Self)
}
extension Factory {
init(factory: () -> Self) {
self = factory()
}
}
class Animal {
var emoji: Character { return "❓" }
}
class Cat : Animal {
override var emoji: Character { return "🐱" }
}
class Dog : Animal {
override var emoji: Character { return "🐶" }
}
extension Animal : Factory {
convenience init(type: String) {
self.init(factory: {
switch type {
case "dog": return Dog()
case "cat": return Cat()
default: return Animal()
}
})
}
}
assert(Animal(type: "dog").emoji == "🐶")
I believe this is *NOT* a right way of implementing factory initializers.
We should introduce a proper "factory initializer" syntax, as discussed in
this ML before.
Any thought?
Do we want to leave them AS IS in Swift4?
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170120/d0c85cfa/attachment-0001.html>
More information about the swift-evolution
mailing list