Может ли тип, соответствующий родительскому протоколу, автоматически считаться соответствующим протоколу, который его расширяет?
Нет. Соответствие дочернему протоколу не выводится из соответствия родительскому: дочерний протокол добавляет новые требования, поэтому тип должен явно объявить соответствие ему и реализовать все дополнительные требования.
Обратное направление работает: соответствие дочернему протоколу включает соответствие его родительскому протоколу.
Наследование протоколов в Swift предназначено для поэтапного расширения абстракции. Базовый протокол задаёт минимальный контракт, а дочерний добавляет более специализированные возможности, не изменяя смысл уже существующих требований.
Такой подход позволяет generic-коду принимать любой тип с базовыми возможностями или требовать более строгий контракт. Автоматическое обратное выведение нарушило бы эту границу: наличие базовых операций не доказывает наличие специализированных.
Представим протоколы для чтения данных и для чтения с поддержкой перемотки. Тип может уметь читать последовательность, но не поддерживать перемещение назад или переход к произвольной позиции.
Если бы соответствие базовому протоколу автоматически означало соответствие дочернему, generic-функция с ограничением на специализированный протокол могла бы вызвать операцию, которой у типа фактически нет. Поэтому Swift требует явного объявления более сильного соответствия.
Дочерний протокол наследует требования родительского, но не добавляет соответствие ему существующим типам. Объявление соответствия — это отдельное утверждение типа: оно сообщает компилятору, что тип гарантирует весь контракт дочернего протокола.
Здесь FileReader соответствует Readable, но не Seekable. Чтобы сделать его аргументом inspect, нужно явно объявить соответствие Seekable и реализовать seek(to:); одной реализации read() недостаточно.
В generic-ограничении T: Seekable проверяется именно соответствие дочернему протоколу. Компилятор не рассматривает такое ограничение как эквивалентное T: Readable, хотя любой Seekable также является Readable.
У этого правила есть важное последствие для проектирования API: добавление нового дочернего протокола не меняет автоматически набор соответствий уже существующих типов. Это сохраняет предсказуемость, но требует явно обновлять типы, которые действительно поддерживают расширенный контракт.
В библиотеке есть тип DataReader, реализующий базовый протокол чтения. Позже появляется дочерний протокол RandomAccessReader, требующий чтение по смещению, и generic-сервис архивирования принимает только RandomAccessReader.
Можно изменить ограничение сервиса на базовый протокол. Плюс — больше типов станет совместимыми; минус — сервис потеряет возможность безопасно использовать операции произвольного доступа.
Можно объявить DataReader соответствующим дочернему протоколу без реальной поддержки перемещения. Это неверно: код будет формально проходить проверку типов, но нарушит контракт и может возвращать некорректные данные.
Правильное решение — проверить, поддерживает ли DataReader требуемую семантику, и только затем явно добавить соответствие RandomAccessReader с корректной реализацией нового требования. Если поддержки нет, следует оставить базовое соответствие или создать отдельную реализацию.
Нет. Даже если дочерний протокол не добавляет собственных требований, соответствие ему всё равно не возникает автоматически из соответствия родительскому. Пустой дочерний протокол может быть маркером или отдельной категорией типов, поэтому его соответствие также должно быть явно заявлено.
Нет. Наличие подходящих методов само по себе не создаёт соответствие протоколу. Тип должен явно объявить конформность, после чего компилятор проверит весь контракт, включая требования родительского протокола.
Это отличается от структурной типизации: Swift использует номинальное соответствие протоколам. Совпадение набора методов без объявления соответствия не позволяет передать тип туда, где требуется протокол.
Такое соответствие допустимо, если все требования доступны компилятору и имеют корректные сигнатуры. Реализация требований может находиться в основном объявлении типа, его расширении или предоставляться default-реализацией из расширения протокола.
Важно различать реализацию и объявление соответствия: расширение может содержать как методы, так и отдельное объявление конформности. Если тип объявляет соответствие только родительскому протоколу, наличие методов дочернего протокола в другом расширении всё равно не создаёт автоматическую конформность к дочернему.