Разберите ошибку: метод структуры изменяет её свойство, но компилятор требует пометить метод как mutating. Что именно означает это требование?
mutating сообщает Swift, что вызов метода может изменить состояние экземпляра структуры или перечисления. Без этого модификатора методы значимых типов считаются неизменяющими экземпляр, поэтому компилятор запрещает присваивание его свойствам.
Вызвать mutating-метод можно только на изменяемом экземпляре, обычно объявленном через var. Экземпляр, связанный через let, изменять нельзя.
Swift активно использует значимые типы — структуры и перечисления. Их изменение должно быть явным, чтобы код сохранял предсказуемую семантику значения и не скрывал побочные эффекты за обычным вызовом метода.
Явная отметка мутации также помогает компилятору контролировать доступ к памяти и согласовать изменяющие операции с правилами неизменяемости и эксклюзивного доступа к данным.
Если метод структуры увеличивает счётчик, меняет состояние конечного автомата или обновляет элемент коллекции, он фактически изменяет сам экземпляр. Объявление такого метода без mutating противоречит контракту обычного метода структуры.
Неверное решение приводит к ошибке компиляции. Даже после добавления mutating вызов всё равно будет запрещён для экземпляра, объявленного как let, поскольку модификатор метода не отменяет правило неизменяемости привязки.
Для структур и перечислений методы по умолчанию не могут изменять self или его хранимые свойства. Модификатор mutating расширяет этот контракт: метод получает право заменить состояние экземпляра, включая возможность присвоить новое значение всему self.
mutating относится к экземпляру, а не к отдельному свойству. Поэтому метод может изменять несколько свойств, вызывать другие изменяющие методы или полностью заменить self.
Модификатор применяется к методам структур и перечислений. Для классов он не нужен: экземпляр класса является ссылочным типом, и метод класса может изменять его хранимые свойства без mutating. При этом ссылка на объект, объявленная через let, остаётся неизменяемой как ссылка, но сам объект обычно может менять внутреннее состояние.
В протоколах mutating тоже имеет значение. Если требование протокола помечено как mutating, тип-значение должен реализовать его с возможностью изменения экземпляра; класс может соответствовать такому требованию без добавления mutating, поскольку его методы способны менять объект через ссылку.
Модификатор не означает автоматическое копирование структуры и не делает операцию потокобезопасной. Он лишь описывает право метода изменить экземпляр; вопросы копирования, совместного доступа и синхронизации решаются другими механизмами Swift.
В приложении состояние фильтра поиска представлено структурой. Метод перехода к следующему состоянию изменяет несколько полей и используется внутри обработчика пользовательского ввода.
Рассматривались два варианта:
mutating. Это сохраняет значение как самостоятельный снимок и делает изменение явным, но требует передавать экземпляр как изменяемый и учитывать ограничения let.Выбран второй вариант. Для состояния фильтра важны предсказуемое копирование, удобное тестирование и отсутствие скрытого общего состояния, поэтому явный mutating лучше отражает модель данных. Если состояние хранится в неизменяемом объекте или доступно только для чтения, метод вызывается после получения изменяемой копии и последующего присваивания результата обратно.
1. Можно ли внутри mutating-метода присвоить новое значение всему self?
Да. mutating разрешает изменять не только отдельные свойства, но и сам экземпляр целиком. Это полезно для структур и перечислений, когда переход в новое состояние проще выразить созданием другого значения, чем последовательным обновлением его полей.
Однако новое значение должно соответствовать типу экземпляра. После такой замены вызывающий код увидит обновлённое значение только при работе с изменяемой привязкой или изменяемым хранилищем свойства.
2. Почему let-ссылка на класс обычно допускает изменение свойств, а let-структура — нет?
let фиксирует саму ссылку на объект класса, но не обязательно состояние объекта, на который она указывает. Поэтому метод класса может изменить свойства этого объекта, не переназначая ссылку.
Структура хранится как значение, и её изменение считается изменением самого значения, связанного с let. Такая операция запрещена. Это одно из практических различий между значимой и ссылочной семантикой.
3. Чем mutating отличается от inout?
mutating — часть объявления метода, описывающая возможность метода изменить экземпляр значения. inout — способ передачи аргумента функции или метода с временным эксклюзивным доступом для чтения и записи.
mutating-метод может вызываться без явного inout в месте вызова. Компилятор сам применяет необходимые правила доступа к изменяемому экземпляру. inout используется в сигнатуре параметра, когда функция должна изменять переданное значение независимо от того, является ли операция методом.