Протокол Swift с предложением "where Self"

В дополнение к этому синтаксису с расширением протокола:

protocol P {}
extension P where Self : UIView {}

... Я случайно обнаружил, что вы можете использовать одно и то же предложение where в самом протоколе:

protocol P where Self : UIView {}

Обратите внимание, что это не то же самое, что и предложение where, ограничивающее общий протокол, и сам не делает P общим протоколом.

Мои эксперименты показывают, что здесь можно использовать только двоеточие, а вещь после двоеточия должна быть классом или протоколом (который может быть общим).

Мне стало любопытно: как это удалось избежать моего уведомления? Поэтому я отправился на поиски свидетельств того, когда он возник. В Swift 3.0 прежний синтаксис легален, но не последний. В Swift 3.3 оба являются законными. Таким образом, последний синтаксис должен был быть тихо введен в нечто вроде Swift 3.2. Я говорю "спокойно", потому что ничего не могу найти в примечаниях к выпуску.

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

Ответ 1

Возможность наложения ограничений суперкласса на объявления протоколов (то есть возможность определять protocol P where Self: C где C - тип класса) была преждевременным следствием
SE-0156, и синтаксис должен был быть отклонен в Swift 4.x, пока функция не была реализована. Попытка использовать эту функцию в Swift 4.x может привести к неправильной компиляции и сбоям, поэтому я бы не использовал ее до Swift 5.

В Swift 5 (Xcode 10.2) эта функция теперь реализована. Из заметок о выпуске:

Протоколы теперь могут ограничивать свои соответствующие типы теми, которые подклассируют данный класс. Поддерживаются две эквивалентные формы:

protocol MyView: UIView { /*...*/ }
protocol MyView where Self: UIView { /*...*/ } 

Swift 4.2 принял вторую форму, но она не была полностью реализована и иногда могла вылетать во время компиляции или во время выполнения. (SR-5581) (38077232)

Этот синтаксис накладывает ограничение суперкласса на MyView которое ограничивает соответствующие типы теми, которые наследуют (или являются) UIView. Кроме того, использование MyView семантически эквивалентно экзистенциальному классу (например, UIView & MyView) в том UIView & MyView что вы можете получить доступ как к членам класса, так и к требованиям протокола к значению.

Например, расширив пример заметок о выпуске:

protocol MyView : UIView {
  var foo: Int { get }
}

class C : MyView {} // error: 'P' requires that 'C' inherit from 'UIView'

class CustomView : UIView, MyView {
  var foo: Int = 0
}

// ...

let myView: MyView = CustomView(frame: .zero)

// We can access both 'UIView' members on a 'MyView' value
print(myView.backgroundColor as Any)

// ... and 'MyView' members as usual.
print(myView.foo)