Dave
Dave

Reputation: 12638

Why would you use `init!` instead of `init?` when implementing a Failable Initializer in Swift?

The Swift documentation Initialization: Failable Initializers details how to use init? to create a failable initializer, which is an initializer that returns an optional of the type it initializes. As with all optionals, it could be nil or non-nil.

The docs also mention you can use init! to create a failable initializer, which returns an implicitly unwrapped optional of the type it initializes (see the The init! Failable Initializer section). This unwraps the optional and indicates it "should" be non-nil. If it is nil and you accessed it without checking, something a programmer may skip since it is marked as "should be non-nil", a runtime error will be generated.

Two questions:

  1. When/Why would you use init! instead of init? when implementing a failable initializer?
  2. Since init! always returns an implicitly unwrapped optional which "should" be non-nil, why wouldn't you just use init instead of init!?

Upvotes: 3

Views: 380

Answers (3)

KleMiX
KleMiX

Reputation: 332

You can delegate from init? to init! and vice versa, and you can override init? with init! and vice versa.

That means you can convert some init? to init! if you are pretty sure it won't fail or vice versa. In the documentation there is example of:

enum TemperatureUnit {
    case Kelvin, Celsius, Fahrenheit
        init?(symbol: Character) {
        switch symbol {
        case "K":
            self = .Kelvin
        case "C":
            self = .Celsius
        case "F":
            self = .Fahrenheit
        default:
            return nil
        }
    }
}

You could have some similar code and you could initialize it only with constants, not using user input, or some other sources of input. So you are pretty sure it won't fail, why not make it init! ?

Upvotes: 1

Antonio
Antonio

Reputation: 72760

You can use it in classes to override a failable initializer and make it non failable.

The same caution must be applied when dealing with implicitly unwrapped: it can be dangerous because it has the power to make the app crash.

Upvotes: 0

Rob Napier
Rob Napier

Reputation: 299455

In the vast majority of cases you should use init rather than init!. There are a few cases where init! is necessary. The most common in my experience is when a "must succeed" initializer wants to call a failable initializer. Consider this case:

struct NotEmpty {
    let something: String
    init?(something: String) {
        guard !something.isEmpty else { return nil }
        self.something = something
    }

    init() {
        self.init(something: "unknown")! // <-- This is illegal
    }
}

We know that init() will succeed because we're passing a non-empty string. But there's no way to express that with a standard initializer (and the compiler can't prove it's true anyway). Instead, we need to use an init!:

init!() {
    self.init(something: "unknown")
}

Now the caller can treat the result as though it were non-optional (which it really is), even though according to the types it could have failed. That would be a programming error and you'd crash. The ! here basically says "yeah, I know it could fail, but I promise it never will." And in Swift, "promise" means "or else please crash."

Upvotes: 6

Related Questions