Search code examples
swifthashableswift-compiler

why do i receive this warning to implement 'hash(into:)' on a type that conforms to a protocol with at default implementation


The code below shows the warning:

 ❯ swiftc demo.swift                                                                                                                                                                       [13:06:57]
Swift.RawRepresentable:2:27: warning: 'Hashable.hashValue' is deprecated as a protocol requirement; conform type 'PlaybackSpeed' to 'Hashable' by implementing 'hash(into:)' instead
    @inlinable public var hashValue: Int { get }

The enum PlaybackSpeed should have the functions resetSettings() & hash(into:) from the default implementation so this warning should not be generated.

Is my understanding wrong or is this a compiler bug?

protocol Settingsable {
  func resetSettings()
}

protocol SettingsSelectable: Hashable, Settingsable {
  var display: String { get }
}

extension SettingsSelectable {
  func hash(into hasher: inout Hasher) {
    hasher.combine(display)
  }
}

extension SettingsSelectable {
    func resetSettings() {
      print("These have been reset")
    }
}

enum PlaybackSpeed: Int, SettingsSelectable {
  case half
  case standard
  case onePointFive
  case double

  var display: String {
    switch self {
    case .half:
      return "0.5x"
    case .standard:
      return "1.0x"
    case .onePointFive:
      return "1.5x"
    case .double:
      return "2.0x"
    }
  }
}

Solution

  • This code has two default implementations for hash(into:). One from Int, and one from SettingSelectable.

    I'm not certain if this is defined behavior. My expectation is that the Int implementation is used and the SettingsSelectable extension is ignored. In any case, the diagnostic is not very good. I suggest opening a defect about that.

    You can fix this error by removing the Int, or by explicitly implementing hash(into:) so it's clear which one you mean. Or you could create another layer of protocols:

    protocol SettingsSelectableBase: Hashable, Settingsable {
      var display: String { get }
    }
    
    protocol SettingsSelectable: SettingsSelectableBase {}
    
    // Only give the default to things that ask for it, not to Base conformers
    extension SettingsSelectable {
      func hash(into hasher: inout Hasher) {
        hasher.combine(display)
      }
    }
    
    extension SettingsSelectableBase {
        func resetSettings() {
          print("These have been reset")
        }
    }
    
    enum PlaybackSpeed: Int, SettingsSelectableBase { ... }