Why does casting AnyHashable to NSObject always succeed?

While experimenting with AnyHashable, I was surprised that the following code actually works.

import Foundation

struct S: Hashable {
    var a: Int
}

let hashable = AnyHashable(S(a: 5))
let object: NSObject = hashable as NSObject

print(object.isEqual(S(a: 5))) // prints true

How is it that AnyHashable which is a struct can always be cast to an NSObject even when wrapping a value type?

i'm not certain about the details, but the SIL shows that the cast is calling the function _bridgeAnythingToObjectiveC, which, if it works as named, i would expect to produce this effect :sweat_smile:.

surprisingly the cast still compiles if you remove the Foundation import and swap NSObject for AnyObject, and even if you pass the -disable-objc-interop frontend flag (godbolt):

struct S: Hashable {}

func test(_ a: AnyHashable) {
    let obj = a as AnyObject // ✅
    _ = obj
}
test(AnyHashable(S()))

there is this code here in the implementation of AnyHashable which might be related, but also seems like it maybe shouldn't work for non-Darwin targets (yet still does)?

That's not a direct comparison.

Given

struct S: Hashable { }

S() as AnyObject // Compiles
S() as AnyHashable // Compiles

import typealias Foundation.NSObject

S() as NSObject // Cannot convert value of type 'S' to type 'NSObject' in coercion
S() as AnyHashable as NSObject // Compiles
1 Like

You can box any copyable value type in AnyObject and, by extension, in NSObject. This is by design. :slight_smile: