Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
693 views
in Technique[技术] by (71.8m points)

casting - Tuple "upcasting" in Swift

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

error: cannot express tuple conversion '(String, Bool)' to '(String, Any)'

But this should work since Bool can be casted safely to Any with as. Almost the same error gets thrown if you do something like that:

let any: Any = ("String", true)
any as! (String, Any) // error
any as! (String, Bool) // obviously succeeds

error:

Could not cast value of type '(Swift.String, Swift.Bool)' to '(protocol<>, protocol<>)'

So is there any workaround especially for the second scenario? Because you cannot even cast Any to any tuple (Any, Any) where you could cast the elements separately.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...