[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