AnyHashable and implicit type conversions

In Swift, AnyHashable is a concrete wrapper type that is used to erase a Hashable's concrete type, especially useful in scenarios where we want to be able to mix hashables of different types.

Interestingly and conveniently, Swift allows us to convert any hashable into an AnyHashable implicitly:

let h: AnyHashable = 1

Contrary to what we might expect from seeing this, AnyHashable is not actually a supertype of Hashable (which might omit its associated type requirements), but it is actually a concrete subtype. It seems that given the type hierarchy, the above should not be legal (Int is not an AnyHashable). That suggests there is some sugar at play.

I have been unable to find this mechanism described either in Swift's public documentation, Apple's reference API, or the Swift Programming Language book. Can someone explain the mechanism involved, perhaps point to a reference, and let me know whether any other such implicit type conversion mechanisms exist in the language?

It's "magic," meaning its behavior (see, for instance, the implementation of its runtime support) is not expressible in Swift itself. It's not "sugar" in the sense of a syntax that could be desugared into less ergonomic Swift: there's nothing you can write in Swift that would reproduce the behavior of AnyHashable.

The custom subtyping relationship between Optional<T> and T, and the covariant generics that standard library collections (Array, etc.) can use but not others, are other examples of "magic" as it relates to Swift types.

3 Likes

This behavior is probably based on the following:

There are other special methods that allow you to conditionally cast an instance of AnyHashable back to the Hashable type that it wraps using as?. You should read through this file.

The standard library type AnyHashable is written partially in C++ specifically so that it can find that common ancestor implementing Equatable.