Что делает модификатор `mutating` в Swift и когда он применяется?

Ответ

Ключевое слово mutating используется для пометки методов структур (struct) и перечислений (enum), которые изменяют свои собственные свойства или присваивают новый экземпляр self.

Зачем это нужно? Структуры и перечисления — это value-типы. По умолчанию их методы не могут изменять свойства, так как они работают с неизменяемой копией (self). Модификатор mutating явно указывает, что метод будет изменять экземпляр.

Пример использования:

struct Counter {
    private var value = 0

    // Метод изменяет свойство `value`, поэтому требуется `mutating`
    mutating func increment() {
        value += 1
    }

    // Можно полностью заменить `self`
    mutating func reset(to newValue: Int) {
        self = Counter(value: newValue)
    }
}

var myCounter = Counter()
myCounter.increment() // Работает, так как `myCounter` — переменная (var)
// let constantCounter = Counter()
// constantCounter.increment() // Ошибка компиляции: нельзя вызвать mutating метод для константы

Важные правила:

  1. Применяется только к методам структур и перечислений.
  2. Может быть вызван только для переменных экземпляров (var), а не для констант (let).
  3. В классах не используется, так как они — reference-типы и их методы могут изменять свойства по умолчанию.

Ответ 18+ 🔞

Смотри, вот есть у тебя структура или перечисление — они же по жизни value-типы, как картошка в мешке: свою копию везде таскают. Так вот, представь, что методы у них по умолчанию — святые, блядь, монахини. Пришли, посмотрели на свойства, вздохнули и ушли. Изменить нихуя не могут, потому что работают с какой-то своей, отдельной копией экземпляра.

А теперь, сука, возникает вопрос: а как же этой монахине дать в руки отвёртку и разрешить что-то внутри починить? Вот для этого и придумали волшебное слово mutating. Это как пропуск в святая святых, разрешение на пиздёж и разврат внутри самой структуры.

Ты этим словом как бы говоришь компилятору: «Слушай, дружище, этот метод сейчас будет хуярить по свойствам self или вообще всю твою структуру на новую менять. Не паникуй, я в курсе».

Смотри, как это выглядит в деле:

struct Счётчик {
    private var значение = 0

    // Без mutating тут была бы ошибка: мол, нельзя менять свойство у неизменяемого self
    mutating func увеличить() {
        значение += 1 // Ёбта, вот она — измена!
    }

    // А тут мы вообще self под ноль обнуляем и новый на его место ставим — полный пиздец, поэтому mutating обязателен
    mutating func сбросить(на новоеЗначение: Int) {
        self = Счётчик(значение: новоеЗначение)
    }
}

var мойСчётчик = Счётчик()
мойСчётчик.увеличить() // Всё ок, потому что мойСчётчик — переменная (var), с ней можно творить хуйню.

// А теперь попробуй с константой:
let константныйСчётчик = Счётчик()
// константныйСчётчик.увеличить() // А вот тут, блядь, компилятор тебе въебет ошибку сразу: «Не-не-не, дружок, ты с константой что собрался? Mutating метод вызывать? Иди нахуй».

И главные правила, которые в голове держать надо, а то огребешь:

  1. mutating — это исключительно для структур (struct) и перечислений (enum). Классы — это reference-типы, у них там свои порядки, они по умолчанию могут всё менять, им это слово как собаке пятая нога.
  2. Вызвать такой развратный метод можно только у переменной экземпляра (объявленной через var). Если экземпляр — константа (let), то забудь, как страшный сон. Компилятор не пропустит, ибо логично: константу менять нельзя, ёпта.
  3. Внутри mutating метода ты не только свойства поменять можешь, но и вообще весь self на новый экземпляр подменить — полная власть, ядрёна вошь!

Короче, mutating — это твой легальный способ сказать структуре: «Расслабься, сейчас будем тебя менять изнутри». Без него — никак, только смотреть.