Search code examples
swiftoption-typeinout

Are implicitly unwrapped optionals truly optionals?


In Swift 4.0, the following code doesn't compile:

var str: String!
func someFunc(_ s: inout String?) {}
someFunc(&str)

Now I imagine that str is of type String? in actuality, and the Swift compiler seems to agree:

Cannot pass immutable value of type 'String?' as inout argument

I can fix this by either changing the the variable to type String? or the function parameters to (_ s: inout String!) but I don't understand why I have to. Swift already seems to agree that var str : String! is "of type 'String?'" — so why won't it let me pass it in here?

Is there another option I can use to keep my variable implicitly unwrapped, but still pass it to a function that modifies an optional?

I tried someFunc(&(str?)) and that just makes things weirder — then Swift complains:

Cannot pass immutable value of type 'String!' as inout argument".

So str is a String? but can't be passed as a String?, while str? is a String!?!

That code was actually:

var str: String!
func someFunc(_ x: inout String!) {}
someFunc(&(str?))

So maybe Swift is mistakenly saying the type of the parameter, rather than the value passed, in its error message…or something?


Solution

  • This is a known bug in the swift compiler. Hamish says in a comment this is fixed in a Swift 4.1 snapshot, so it may be fixed in the next Xcode release (9.3).

    You can work around this by getting rid of the implicitly-unwrapped optional (IUO), which should be avoided anyway. Depending on why it's currently an IUO, either:

    var str: String?
    func someFunc(_ x: inout String?) {}
    someFunc(&str)
    

    or

    var tmp: String?
    func someFunc(_ x: inout String?) {}
    someFunc(&tmp)
    let str = tmp!
    

    I strongly recommend the first, avoid force-unwrapping unless absolutely necessary.