Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding why this Swift tuple assignment isn't allowed

The following code is fine:

func intTest() -> Int? {
    var res : Int

    res = 5

    return res
}

There is no problem returning a non-optional Int from a method with a return type of optional Int.

Now lets see what happens when the return value is a tuple of optional values:

func tupleTest() -> (Int?, Int?) {
    var res : (Int, Int)

    res = (5, 5)

    return res
}

The return res line results in the error:

error: cannot express tuple conversion '(Int, Int)' to '(Int?, Int?)' (aka '(Optional, Optional)')

Why does Swift not allow this?

I certainly understand why this would be an error in the other direction (optional to non-optional) but why can't a tuple accept a non-optional value in the place of an optional when a non-tuple works fine?

I've seen Swift tuple to Optional assignment but that doesn't cover why you can't do the assignment, it just covers how to solve it.

like image 395
rmaddy Avatar asked Oct 06 '16 03:10

rmaddy


1 Answers

The reason that's happening is because of a type mismatch. Consider the following case:

let myClosure: (Int) -> ()
let myOtherClosure: (Int?) -> ()

The type of myClosure is (Int) -> () while myOtherClosure is of type (Int?) -> () which makes them fundamentally different types, despite the similarity of the parameters. When Swift is looking at these two constants, it's evaluating the type as a whole, not the individual pieces. The same is happening with your tuple; it's looking at the tuple type signature as a whole unit, not breaking down the parameters and recognizing that they're non-optional versions of the same type.

Converting from Int to Int? works because they're the same type, one is just an optional. Going from (Int, Int) to (Int?, Int?) doesn't because the parameters of the tuple are different therefore causing the overall type to be different, hence your error.

Taking a look at one of your examples:

 func tupleTest() -> (Int?, Int?) {
    let first: Int = 1
    let second: Int = 2
    let res: (Int?, Int?) = (first, second)
    return res
 }

This works because even though the values are non-optional integers, the tuple type is marked as (Int?, Int?) whereas:

func tupleTest() -> (Int?, Int?) {
    let first: Int = 1
    let second: Int = 2
    let res = (first, second)
    return res
}

doesn't compile because now res is of type (Int, Int). I'm not an expert on the Swift compiler but my guess is that the way the type system works is it doesn't deconstruct each individual part of a function or a tuple and recognize that the corresponding return parameter is the same type, just an optional.

like image 151
barndog Avatar answered Oct 20 '22 16:10

barndog