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

2

7

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"
Dharma answered 4/5, 2016 at 9:46 Comment(0)
D
9
/// 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.

Dameron answered 4/5, 2016 at 10:52 Comment(3)
Last two paragraphs raise the most important points here. And honestly, NSObject (or AnyObject which wouldn't work here but I see it used all the time) isn't that much more specific than Any.Airlee
@originaluser2 your explanation is really helpful, thanks a lotDharma
@Dharma Happy to help :)Dameron
A
3

Because it won't auto recognize array of Any

it will work if you define it as

let testArray2 :[Any] = [2, "world"]

the Foundation library imports the NS API, which automatically converts the 2 to NSNumberand "world" to NSString, converting it automatically to array of NSObject

Atchley answered 4/5, 2016 at 10:10 Comment(3)
1. Answer: Funny, he asks why and you only say because it is so. 2. Answer: If you use import Foundation, 2 is Int and "world" is String. Just because there is no native Swift type that can be found (who knows why it does not recognize [Any]) it takes the [NSObject].Attainder
@iGodric, hey, you are wrong.1. look at his error: Type of expression is ambiguous without more content, It can't auto recognize it because thats how the compiler works. 2. an array of NSObject must contain NSObjects . if you will do import Foundation and do the next code print(testArray[0].dynamicType) print(testArray[1].dynamicType) the output will be __NSCFNumber which is NSNumber (and not Int) and _NSContiguousString which is NSString and not String. thanks for the Funny comment.Atchley
To 1. Yes, but an explanations saying "that is how the compiler works" is no explanation. To 2. That what you said is correct, but you are making assumptions in my comment, which I did not say. Yes in that particular case when importing Foundation and creating an array with multiple types 2 is NSNumber and "world" is NSString but only because the inferred type is [AnyObject]. But importing Foundation does not automatically convert 2 to NSNumber per se, that is what I am trying to say. Your answer can be misleading. My first comment may sound to harsh.Attainder

© 2022 - 2024 — McMap. All rights reserved.