Why is overriding a non-failable init with a failable init not allowed?

Swift will allow me to override a failable initializer with a non-failable initializer but not the other way around:

class Animal {
    var name: String
    init(name: String) {
        self.name = name
    }
}
class Dog: Animal {
    var isGood = true
    // Error: Failable initializer 'init(name:)'
    // cannot override a non-failable initializer
    override init?(name: String) {
        if name.isEmpty { return nil }
        super.init(name: name)
    }
}

The docs cover this with a small note:

Note

You can override a failable initializer with a nonfailable initializer but not the other way around.

What is the reason for this restriction?

Is it similar to not being allowed to override a read-write property with a read-only property, that the subclass can’t be more restrictive?

Yep, pretty much. Same reason you cannot override a method that returns some type T with one that returns a T?.

Because if you could, then what happens if you do this:

class Base {
  convenience init(x: Int) {
    self.init(y: x)
    print(self)
  }

  init(y: Int) {}
}

class Sub: Base {
  override init?(y: Int) { return nil }
}

Sub(x: 0)  // what does this print?
2 Likes

The superclass’s initializer defines a contract for what callers can expect.

A non‑failable init promises to always produce an instance; a failable init? only promises to maybe produce one.

When overriding, you’re allowed to strengthen this contract but not weaken it.

Overriding a failable initializer with a non‑failable one is fine: callers that are prepared for nil still work if they never actually get nil.

But overriding a non‑failable initializer with a failable one would break the contract, because callers relying on a guaranteed instance could suddenly receive nil.

(full disclosure: I used AI to help clarify my text in this post :robot:)

3 Likes

You can also think of a call to an initializer as being pretty much the same as calling a static method that either returns Self, or Self? (if it’s failable). A similar rule is enforced for method overrides, where you can override a method returning T? with one returning T but not vice versa.

Overriding a property works the same if the property is read only. If it has a setter, the override is required to have the same exact type.

2 Likes