Почему новое обязательное требование нельзя добавить в extension уже объявленного протокола?
extension протокола может добавить методы с реализацией по умолчанию, но не новое обязательное требование для всех соответствующих типов. Набор требований протокола фиксируется в его объявлении, поэтому добавление требования в extension не заставляет существующие conforming-типы его реализовывать.
Расширения протоколов предназначены для вынесения общего поведения, вспомогательных методов и реализаций по умолчанию за пределы основного объявления. Это позволяет развивать API и переиспользовать код, не дублируя реализацию в каждом типе.
При этом соответствие протоколу должно иметь согласованный набор требований. Если бы extension мог задним числом добавлять обязательные требования, ранее созданные соответствия пришлось бы пересматривать, что ухудшило бы совместимость исходного кода и усложнило бы работу с уже скомпилированными модулями.
Предположим, протокол уже используется десятками типов. Добавление нового обязательного метода в его объявление требует обновить каждый тип, соответствующий протоколу. Это может быть намеренным изменением контракта, но оно является потенциально ломающим изменением.
Если поместить метод только в extension, проблема не исчезает автоматически: такой метод становится обычным членом расширения, а не частью контракта протокола. Типы не обязаны предоставлять собственную реализацию, и компилятор не проверяет наличие этого метода при проверке соответствия.
Требования протокола объявляются внутри самого protocol. Расширение может предоставить для них реализацию по умолчанию, но не превращает новый метод в обязательное требование.
Здесь reset является требованием, потому что объявлен в протоколе. Session получает реализацию из extension и всё равно считается соответствующим протоколу. Если тип объявит собственную реализацию, она будет использована как witness этого требования.
Если написать новый метод только в extension, он будет доступен типам, для которых применимо это расширение, но не станет частью проверки соответствия. Кроме того, такой метод может участвовать в статическом разрешении вызова как член protocol extension, а не как динамически выбранная реализация требования протокола.
Когда контракт действительно нужно расширить, есть три основных варианта:
Отдельного понятия «необязательное требование протокола» в обычной модели Swift нет. Реализация по умолчанию делает требование удобным для conforming-типа, но не делает его необязательным: требование всё равно должно быть объявлено в самом протоколе.
В библиотеке есть протокол Cache, которым уже пользуются разные реализации. Позже появляется операция очистки кэша. Добавление clear только в extension не гарантирует, что каждая реализация действительно умеет очищать данные: метод может быть пустой реализацией по умолчанию и не отражать реальные возможности типа.
Изменение исходного протокола даёт строгий контракт, но потребует проверить и обновить всех существующих conforming-типов. Это подходит, если библиотека контролирует все реализации или готова выпустить ломающую версию API.
Более безопасный вариант — создать отдельный протокол ClearableCache, унаследованный от Cache, и объявить clear в нём. Тогда старые реализации сохраняют исходное соответствие, а типы с поддержкой очистки явно заявляют дополнительную возможность. Такой подход выбран, если очистка является отдельной capability, а не обязательным свойством любого кэша.
1. Можно ли объявить новое требование внутри extension протокола, если сразу дать ему реализацию по умолчанию?
Нет. Метод в extension может выглядеть как требование, но для компилятора он остаётся членом расширения. Он не входит в witness table протокола и не проверяется как часть соответствия. Чтобы получить настоящее требование, его нужно объявить в теле протокола, а реализацию по умолчанию можно вынести в extension.
2. Станет ли метод из extension динамически переопределяемым для existential-значения протокола?
Не автоматически. Если метод не объявлен в протоколе, вызов через existential-значение разрешается как вызов члена extension, поэтому реализация типа с тем же именем не обязана быть выбрана полиморфно. Если нужна динамическая диспетчеризация через протокол, метод должен быть объявлен требованием протокола.
3. Что выбрать, если новая операция нужна только части реализаций?
Обычно следует создать отдельный протокол-возможность, например уточняющий протокол с новым требованием. Условное extension может ограничить доступность вспомогательной реализации, но само по себе не создаёт нового обязательства для conforming-типов. Это явно разделяет базовый контракт и дополнительную возможность, не заставляя несвязанные типы поддерживать неподходящую операцию.