Swift: ограничить универсальную переменную классом или протоколом - PullRequest
0 голосов
/ 09 ноября 2019

Поскольку KeyPath не может быть приведен к супертипам в Swift, я хочу написать стертую версию, которая представляет собой KeyPath, значение которого может быть приведено к определенному протоколу или супертипу:

public struct PropertyGetter<Root, ValueType> {
  private let keyPath: PartialKeyPath<Root>

  public init<T: ValueType>(_ keyPath: KeyPath<Root, T>) {
    self.keyPath = keyPath
  }

  public func get(_ instance: Root) -> ValueType {
    return instance[keyPath: self.keyPath] as! ValueType
  }
}

Компилятор справедливо жалуется, что

тип 'T' ограничен непротокольным типом не типа 'ValueType.Type'

как ValueType потенциально может быть структурным типом.

Итак, как нам правильно ограничить это? Это означает, что

  1. ValueType является либо типом класса, либо протоколом, т.е. подклассифицируемый / реализуемый
  2. T ограничен для соответствия ValueType, т.е. x as! ValueType должен гарантированно работать, где x: T

Обратите внимание, что действительно возможно написать такую ​​структуру стирания типов, когда тип протокола фиксирован. Например, класс, который принимает только KeyPath s, указывающие на CustomStringConvertible членов:

public struct CustomStringConvertibleGetter<Root> {
  private let keyPath: PartialKeyPath<Root>

  public init<T: CustomStringConvertible>(_ keyPath: KeyPath<Root, T>) {
    self.keyPath = keyPath
  }

  public func get(_ instance: Root) -> CustomStringConvertible {
    return instance[keyPath: self.keyPath] as! CustomStringConvertible
  }
}

let getter1 = CustomStringConvertibleGetter(\SomeClass.someString) // works
let getter2 = CustomStringConvertibleGetter(\SomeClass.nonConformingMember) // will throw an error at compile time
...