How are Int and String accepted as AnyHashable?

前端 未结 3 1057
面向向阳花
面向向阳花 2021-01-05 01:18

How come I can do this?

    var dict = [AnyHashable : Int]()
    dict[NSObject()] = 1
    dict[\"\"] = 2

This implies that NSObject

相关标签:
3条回答
  • 2021-01-05 01:57

    Consider that Optional is an enum, which is also a value type – and yet you're freely able to convert a String to an Optional<String>. The answer is simply that the compiler implicitly performs these conversions for you.

    If we look at the SIL emitted for the following code:

    let i: AnyHashable = 5
    

    We can see that the compiler inserts a call to _swift_convertToAnyHashable:

      // allocate memory to store i, and get the address.
      alloc_global @main.i : Swift.AnyHashable, loc "main.swift":9:5, scope 1 // id: %2
      %3 = global_addr @main.i : Swift.AnyHashable : $*AnyHashable, loc "main.swift":9:5, scope 1 // user: %9
    
      // allocate temporary storage for the Int, and intialise it to 5.
      %4 = alloc_stack $Int, loc "main.swift":9:22, scope 1 // users: %7, %10, %9
      %5 = integer_literal $Builtin.Int64, 5, loc "main.swift":9:22, scope 1 // user: %6
      %6 = struct $Int (%5 : $Builtin.Int64), loc "main.swift":9:22, scope 1 // user: %7
      store %6 to %4 : $*Int, loc "main.swift":9:22, scope 1 // id: %7
    
      // call _swift_convertToAnyHashable, passing in the address of i to store the result, and the address of the temporary storage for the Int.
      // function_ref _swift_convertToAnyHashable
      %8 = function_ref @_swift_convertToAnyHashable : $@convention(thin) <τ_0_0 where τ_0_0 : Hashable> (@in τ_0_0) -> @out AnyHashable, loc "main.swift":9:22, scope 1 // user: %9
      %9 = apply %8<Int>(%3, %4) : $@convention(thin) <τ_0_0 where τ_0_0 : Hashable> (@in τ_0_0) -> @out AnyHashable, loc "main.swift":9:22, scope 1
    
      // deallocate temporary storage.
      dealloc_stack %4 : $*Int, loc "main.swift":9:22, scope 1 // id: %10

    Looking in AnyHashable.swift, we can see the function with the silgen name of _swift_convertToAnyHashable, which simply invokes AnyHashable's initialiser.

    @_silgen_name("_swift_convertToAnyHashable")
    public // COMPILER_INTRINSIC
    func _convertToAnyHashable<H : Hashable>(_ value: H) -> AnyHashable {
      return AnyHashable(value)
    }
    

    Therefore the above code is just equivalent to:

    let i = AnyHashable(5)
    

    Although it's curious that the standard library also implements an extension for Dictionary (which @OOPer shows), allowing for a dictionary with a Key of type AnyHashable to be subscripted by any _Hashable conforming type (I don't believe there are any types that conform to _Hashable, but not Hashable).

    The subscript itself should work fine without a special overload for _Hashable keys. Instead the default subscript (which would take an AnyHashable key) could just be used with the above implicit conversion, as the following example shows:

    struct Foo {
        subscript(hashable: AnyHashable) -> Any {
            return hashable.base
        }
    }
    
    let f = Foo()
    print(f["yo"]) // yo
    

    Edit: In Swift 4, both the aforementioned subscript overload and _Hashable have been removed from the stdlib by this commit with the description:

    We have an implicit conversion to AnyHashable, so there's no need to have the special subscript on Dictionary at all.

    Which confirms my suspicion.

    0 讨论(0)
  • 2021-01-05 02:08

    You can find this code, when you cmd-click on [ or ] of dict[NSObject()] = 1 in the Swift editor of Xcode (8.2.1, a little different in 8.3 beta):

    extension Dictionary where Key : _AnyHashableProtocol {
    
        public subscript(key: _Hashable) -> Value?
    
        public mutating func updateValue<ConcreteKey : Hashable>(_ value: Value, forKey key: ConcreteKey) -> Value?
    
        public mutating func removeValue<ConcreteKey : Hashable>(forKey key: ConcreteKey) -> Value?
    }
    

    _AnyHashableProtocol and _Hashable are hidden types, so you may need to check the Swift source code. Simply:

    • _Hashable is a hidden super-protocol of Hashable, so, Int, String, NSObject or all other Hashable types conform to _Hashable.

    • _AnyHashableProtocol is a hidden protocol, where AnyHashable is the only type which conforms to _AnyHashableProtocol.

    So, the extension above is very similar to this code in Swift 3.1.

    extension Dictionary where Key == AnyHashable {
    
        //...
    }
    

    You see, when you write such code like this:

        var dict = [AnyHashable : Int]()
        dict[NSObject()] = 1
        dict[""] = 2
    

    You are using the subscript operator defined in the extension.

    0 讨论(0)
  • 2021-01-05 02:17
    class SomeClass: NSObject {
       var something: String = "something"
    }
    
    var dict = [AnyHashable: Int]()
    var object = SomeClass()
    
    dict = ["a": 1, object: 2]
    
    print(dict["a"]) // result: Optional(1)
    print(dict[object]) // result: Optional(2)
    
    var object2 = SomeClass()
    dict[object2] = 3
    print(dict[object2]) // result: Optional(3)
    
    0 讨论(0)
提交回复
热议问题