1

I came across a heterogeneous dictionary definition like this on a tutorial online:

var mixedMap4 = [AnyHashable(0): "Zero" as Any,
                 AnyHashable(1): 1.0 as Any,
                 AnyHashable("pi"): 3.14 as Any]

I was wondering why the author chose to write AnyHashable(0) instead of 0 as AnyHashable. When I tried this on Swift playground, it also worked. However when I turned "Zero" as Any into Any(0) it gives the following

error: error: The Dictionary.xcplaygroundpage:41:34: error: protocol type 'Any' cannot be instantiated var mixedMap4 = [AnyHashable(0): Any("Zero") ,

Thank you for the answer

tiw
  • 535
  • 1
  • 6
  • 22
  • 2
    `AnyHashable` is an actual type, `Any` is just `protocol` — interface contract for a type. – user28434'mstep Oct 25 '19 at 13:11
  • @user28434 I see.. The error said "protocol type 'Any'" so I thought it was also a type. – tiw Oct 25 '19 at 13:15
  • 1
    @user28434 Although see the bottom paragraph of this page: https://docs.swift.org/swift-book/LanguageGuide/TypeCasting.html which states Any is a type. – tiw Oct 25 '19 at 13:21
  • 2
    I would have written this as: `var mixedMap4: [AnyHashable : Any] = [0: "Zero", 1: 1.0, "pi" : 3.14]` – vacawama Oct 25 '19 at 13:25
  • @vacawama Huh, I didn't expect that to work. Does that mean that `AnyHashable` gets special treatment from the compiler? Because I don't think the same would happen with hand rolled type erasers – Alexander Oct 25 '19 at 13:33
  • @Alexander isn't it just like declaring a dictionary with any two hashable types that already exist? Why would [AnyHashable: Any] act differently? – tiw Oct 25 '19 at 13:40
  • 1
    @tiw `Any` is a protocol, formally speaking, it's the "universal type." It's the supertype of all other types, but that's some compiler magic, because you don't explicitly write "struct MyType: Any". The conformance are implied. The fact that you can write `let i: Any = 0` and have it work is a consequence of this implicit conformance. `AnySequence` is a struct. The fact that you can write `let i: AnyHashable = 1` is a testament to some compiler magic, which makes any `Hashable` conforming type be convertible to `AnyHashable`. But that isn't the norm.Try `let a: AnySequence = []`, for example. – Alexander Oct 25 '19 at 14:36
  • @Alexander, you raise a good question. I thought *this ought to work* and tried it and it did. How exactly Swift does that behind the scenes is unknown to me at this time. – vacawama Oct 25 '19 at 14:43
  • @vacawama, @Alexander, they added this compiler trick when they introduces that `AnyHashable` type in `Swift 3`. `Dictionary` requires `Key` to be `Hashable`. But you can't say `[Hashable: Any]`, because `Hashable` is the protocol-descendant of `Equatable` protocol. And `Equatable` refers to the type implementing it `==` operator declaration. Therefore you can't use it directly, and required to use it via generic parameter. But if you want dictionary with heterotyped keys you can't do that. And that's where `AnyHashable` comes to help. And you need some compiler hacks to make it look prettier. – user28434'mstep Oct 25 '19 at 14:55
  • 2
    AnyHashable is a bit magical and special-cased by the compiler. See https://github.com/apple/swift/blob/d6117a43b7c83f459caf5aa4259758f1aee8ed32/stdlib/public/core/AnyHashable.swift. – Rob Napier Oct 25 '19 at 14:55

1 Answers1

3

The clue is in the error message.

AnyHashable is a struct that type-erases the underlying hashable type, and so can be directly instantiated as an object

Any is a protocol and therefore can't be directly instantiated, although all other types can be complied with it, thus a String such as "Zero" can be cast as Any but Any(String) is meaningless.

To me it all just feels like a bucket load of trouble waiting to happen!

flanker
  • 3,840
  • 1
  • 12
  • 20