Tuple “upcasting” in Swift

后端 未结 2 939
后悔当初
后悔当初 2020-12-20 15:16

If I have a tuple with signature (String, Bool) I cannot cast it to (String, Any). The compiler says:

error: cannot express

相关标签:
2条回答
  • 2020-12-20 15:59

    The Swift compiler will enforce that you are explicit about your types. So if you declare it as (String, Bool) it will not allow the conversion.

    The following works as expected in Playground:

    var specificTuple : (String, Bool) = ("Hi", false)
    var generalTuple  : (Any, Any)     = ("Hi", false)
    
    var gany = generalTuple
    gany.1 = "there"
    gany                           // (.0 "Hi", .1 "there")
    
    var spany = specificTuple
    spany.1 = "there"             // error
    

    You can create the (Any, Any) tuple ad hoc but you will need to decompose it

    var any : (Any, Any)  = (specificTuple.0, specificTuple.1)
    any.1 = "there"
    any                          // (.0 "Hi", .1 "there")
    
    0 讨论(0)
  • 2020-12-20 16:04

    Tuples cannot be cast, even if the types they contain can. For example:

    let nums = (1, 5, 9)
    let doubleNums = nums as (Double, Double, Double) //fails
    

    But:

    let nums : (Double, Double, Double) = (1, 5, 9) //succeeds
    

    The workaround in your case is to cast the individual element, not the Tuple itself:

    let tuple = ("String", true)
    let anyTuple = (tuple.0, tuple.1 as Any)
    // anyTuple is (String, Any)
    

    This is one of the reasons the Swift documentation notes:

    Tuples are useful for temporary groups of related values. They are not suited to the creation of complex data structures. If your data structure is likely to persist beyond a temporary scope, model it as a class or structure, rather than as a tuple.

    I think this is an implementation limitation because Tuples are compound types like functions. Similarly, you cannot create extensions of Tuples (e.g. extension (String, Bool) { … }).


    If you're actually working with an API that returns (String, Any), try to change it to use a class or struct. But if you're powerless to improve the API, you can switch on the second element's type:

    let tuple : (String, Any) = ("string", true)
    
    switch tuple.1 {
    
    case let x as Bool:
        print("It's a Bool")
        let boolTuple = (tuple.0, tuple.1 as! Bool)
    
    case let x as Double:
        print("It's a Double")
        let doubleTuple = (tuple.0, tuple.1 as! Double)
    
    case let x as NSDateFormatter:
        print("It's an NSDateFormatter")
        let dateFormatterTuple = (tuple.0, tuple.1 as! NSDateFormatter)
    
    default:
        print("Unsupported type")
    }
    

    If the API returns Any and the tuple isn't guaranteed to be (String, Any), you're out of luck.

    0 讨论(0)
提交回复
热议问题