Allow returning `.none` in failable inits


(Christian Schnorr) #21

Don't get me wrong, I would like to see self assignment in initializers as much as everyone else here. But since we don't have self = other, we can't have return other or return .some(other).

But even if we could, I would prefer writing self = other (and self = nil), as that matches my intuition of the initializer configuring self, rather than returning a value. I would like to hear other people's thoughts on this.


#22

I’m going to push back against this line of deduction pretty hard. Without commenting on the idea itself, I’m pretty sure there is nothing intrinsically preventing us from allowing one without the other.


(Adrian Zubarev) #23

I rewrote this answer the 3rd time and returned to my original impression. I probably would not support self = nil syntax because it feels ambiguous to me. If that was possible even if your point from earlier where you said that at initialization the value is already created but not yet configured was true, it would require self to be of type Self?, right?! I don't think this correct, but always free to correct me. ;)


(Christian Schnorr) #24

I’m going to push back against this line of deduction pretty hard.

It's not a "you can't have this, so I can't have this" — how would the compiler implement the latter without being able to support the first?


#25

…by having someone write the code for the compiler to support it.


(Christian Schnorr) #26

Sure, that would work. But I doubt that's such an easy starter bug as it was made out to be earlier in this thread, considering how much effort it took just for convenience initializers.


(Christian Schnorr) #27

Let us consider the following example:

class Superclass {
    class func foo() -> Self {
        return self.init()
    }

    required init() {
        self = type(of: self).foo() // let us imagine this would work
    }
}

class Subclass: Superclass {
    required init() {
        self.bar = 0
        super.init()
    }

    init(bar: Int) {
        self.bar = bar
        super.init()
        // what value does self.bar have here?
    }

    let bar: Int
}

If an initializer can change an object's instance, the constant self.bar might have a different before and after calling the superclass's initializer — which is very, very unintuitive.

This problem does not exist with convenience initializers, as those only delegate to a designated initializer and do not access self before.