Search code examples
swiftoption-typeunary-operatorbinary-operators

Swift unary operator with implicitly unwrapped optional


So I asked this question, and it looks like the reason

var num:Int! = 0
num++

doesn't work is because the ++ operator takes an inout parameter, which implicitly unwrapped optionals are not. However, in

var num:Int! = 0
num = num + 1

the + operator works with the implicitly unwrapped optional, meaning that the binary operator doesn't require an inout parameter. So my question is, why do unary and binary operators have different parameter requirements? It seems kind of silly to me to be able to use an Int! with only binary operators, but use an Int with everything.


Solution

  • why do unary and binary operators have different parameter requirements?

    Well, it isn't a unary vs. binary question. There are unary operators that work with Int!. For instance:

    var i: Int! = 17
    var j = -i
    

    - is a unary operator and it works. The question comes back to the issue of inout. The ++ prefix and postfix operators for Int don't work with Int! because the variable is passed as inout (since ++ modifies the original variable in addition to returning a value). inout requires the type to exactly match.

    Note that implicitly unwrapped optionals are still optionals.

    var i: Int! = 17
    var k = i    // k has the type Int!
    var m = i!   // m has the type Int
    

    So passing an implicitly unwrapped optional as an inout variable that requires the non-optional type doesn't work because inout variables need to exactly match the type expected and Int and Int! are two very different types. The variable must either be explicitly unwrapped, or you need to provide an overloaded function that takes the optional type.

    You might ask, why doesn't Swift just unwrap the Int! for you and call ++ with the Int? Well, ++ both modifies the variable and returns a value. If Swift unwrapped the Int! and called ++ with an Int, then the type of the return would be Int. Then you'd have people on StackOverflow asking, "why does var i: Int! = 17; var j = i++ make j an Int instead of an Int! ?". To do it right, ++ needs to return an Int when it is given an Int, and return an Int! when it is given an Int!. So, what is needed in an overloaded function.

    It is possible to overload ++ and make ++ prefix and postfix functions for Int!:

    prefix func ++(inout x: Int!) -> Int! {
        return ++x!
    }
    
    postfix func ++(inout x: Int!) -> Int! {
        return x!++
    }
    
    var i: Int! = 17
    var j = ++i
    print("i = \(i), j = \(j)")  // "i = 18, j = 18"
    j = i++
    print("i = \(i), j = \(j)")  // "i = 19, j = 18"
    

    As to why the Swift designers haven't done this, only they know why.