В протоколе свойство доступно для чтения и записи, но реализация объявляет его как let. Почему Swift отклоняет такую реализацию?
Swift отклоняет такую реализацию, потому что требование get set означает наличие не только чтения, но и доступного setter. Свойство let имеет только getter и после инициализации не может быть изменено, поэтому оно удовлетворяет лишь требованию get.
Протоколы в Swift задают контракт, через который код работает с типом, не зная его конкретной реализации. Для свойств контракт должен описывать не только тип значения, но и допустимые операции: чтение, запись или обе операции.
Такой подход предотвращает ситуацию, когда код получает значение через протокол, ожидает возможность записи, но фактический тип не способен эту запись выполнить.
Если протокол требует get set, любой код, работающий с экземпляром через этот протокол, вправе изменить свойство. Подстановка типа с let нарушила бы этот контракт: чтение было бы возможно, а запись завершилась бы ошибкой уже на уровне реализации.
Важно отличать объявление свойства в конкретном типе от объявления требования в протоколе. let в типе означает неизменяемое хранимое свойство, тогда как var с get set может быть хранимым или вычисляемым свойством с setter.
Свойство протокола с требованием get set должно иметь доступные getter и setter. Для структуры это обычно var с хранимым значением или вычисляемое свойство; для класса также требуется возможность записи, поскольку let не предоставляет setter ни в одном из этих случаев.
Если протокол требует только get, его может удовлетворить как let, так и var. При обращении через такой протокол наличие var у конкретного типа не даёт права записи: статический контракт протокола всё равно разрешает только чтение.
Setter должен быть доступен в том контексте, где используется требование протокола. Поэтому private(set) var обычно не может удовлетворить внешнему требованию get set: запись через значение протокольного типа должна быть реально доступна вызывающему коду.
Для типа-значения запись свойства может изменять весь экземпляр, поэтому setter структуры обычно является изменяющим. Это не меняет основного правила: var предоставляет возможность записи, а let — нет.
Представим протокол настроек, через который экран должен обновлять выбранную тему. Если конкретная структура объявит тему как let, она не сможет соответствовать контракту редактируемых настроек.
Можно было бы ослабить протокол до get, но тогда исчезнет гарантия, что любой объект настроек можно изменить через этот интерфейс. Можно также предоставить вычисляемое свойство с setter, однако это имеет смысл только при наличии хранилища или другого изменяемого источника данных.
Оптимальное решение — оставить get set и реализовать свойство как var, если изменение является частью ответственности типа. Если объект должен быть неизменяемым, лучше изменить контракт на get, чтобы интерфейс честно отражал его семантику.
let удовлетворить требованию свойства только для чтения?Да. Требование var value: T { get } описывает только getter, поэтому его может реализовать как let value: T, так и var value: T. Однако при обращении через протокол свойство будет доступно только для чтения, даже если конкретная реализация использует var.
private(set) var удовлетворить требованию get set?Только если setter имеет достаточную доступность для самого требования и места его использования. Если протокол доступен клиенту, а setter закрыт через private(set), клиент не сможет выполнить обязательную запись через протокольный тип, поэтому такая реализация не соответствует полноценному контракту get set.
var, чтобы удовлетворить get set?Нет. Одного ключевого слова var недостаточно, если свойство фактически не имеет доступного setter. Например, вычисляемое свойство с одним getter является доступным для чтения, но не удовлетворяет требованию записи. Swift проверяет именно наличие требуемых операций и их доступность, а не только форму объявления.