Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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"
like image 388
c41ux Avatar asked May 04 '16 09:05

c41ux


1 Answers

/// 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.

like image 76
Hamish Avatar answered Sep 21 '22 02:09

Hamish