Search code examples
arraysswifttypestype-inferenceanyobject

Why Swift doesn't type inference to Any when put multiple type item in Array


there are two situation make me confuse when develop swift 2.2 by using Xcode 7.1, please see the example below, thanks

First, when import Foundation, I declared an testArray which contains two item, an Integer type 1 and a String type "hello", my question is why Swift type inference testArray to Array(NSObject) instead of Array(Any)

import Foundation
let testArray = [1, "hello"] 
print(testArray.dynamicType) //testArray is Array<NSObject>

Second, when i remove import Foundation, the code below can't be compile, the error message is "Type of expression is ambiguous without more content", my question is why Swift not type inference to Array(Any) in this situation, thanks for help

let testArray2 = [2, "world"]
print(testArray2) 
//can't compile, error message = "Type of expression is ambiguous without more content"

Solution

  • /// The protocol to which all types implicitly conform.
    public typealias Any = protocol<>
    

    Any is just a protocol that all types implicitly conform to – it's not a concrete type itself. Swift cannot infer an array of non-concrete types, which is why it fails to infer Any, but succeeds with NSObject (Int can be bridged to NSNumber, String can be bridged to NSString – and they both inherit from NSObject, which is a concrete type).

    For example, consider this:

    protocol Foo {}
    struct Bar:Foo {}
    struct Baz:Foo {}
    
    let arr = [Bar(), Baz()] // error: Type of expression is ambiguous without more context
    

    Because Foo is a non-concrete type, Swift cannot infer an array of it. You have to explicitly tell the compiler what you want its type to be:

    let arr:[Foo] = [Bar(), Baz()]
    

    You'll also get the same behaviour with AnyObject (as it's a protocol that all classes implicitly conform to – but still not a concrete type):

    class Qux {}
    class Fox {}
    
    let a = [Qux(), Fox()] // error: Type of expression is ambiguous without more context
    
    let a1:[AnyObject] = [Qux(), Fox()] // no error
    

    Why Swift is unable to infer an array of non-concrete types is most likely due to the existing limitations of non-concrete types in the language – currently concrete types are required for most non-trivial operations. See this great Q&A for an example.

    But to be honest, you should really be thinking more about whether you actually need an array of Any. I cannot think of a single practical application of having an array of Any, as because everything implicitly conforms to the elements, they must be guaranteed to do nothing (you can't call a specific method on something that could be anything). Sure you can type-cast, but what's the point in getting back the type safety that you threw away to begin with?

    You should always be as type specific as you can. You could build a wrapper for your values – this could either be a simple struct to wrap a couple of properties, or a type erasure in order to wrap non-concrete types in a pseudo concrete type. At the very least, you should consider creating your own protocol that your array elements conform to.