Для чего в Swift используется ключевое слово `mutating`?

Ответ

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

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

Пример:

struct Point {
    var x = 0.0, y = 0.0

    // Этот метод изменяет свойства структуры
    mutating func moveBy(x deltaX: Double, y deltaY: Double) {
        x += deltaX
        y += deltaY
        // Эквивалентно: self = Point(x: self.x + deltaX, y: self.y + deltaY)
    }
}

var myPoint = Point(x: 1.0, y: 1.0)
myPoint.moveBy(x: 2.0, y: 3.0) // Работает, так как myPoint объявлен через 'var'
print(myPoint) // Point(x: 3.0, y: 4.0)

let constantPoint = Point(x: 1.0, y: 1.0)
// constantPoint.moveBy(x: 2.0, y: 3.0) // Ошибка компиляции: нельзя вызвать mutating метод для константы

Важные детали:

  1. mutating не используется с классами (class), так как они являются reference-типами и их методы могут изменять свойства по умолчанию.
  2. Вызов mutating метода для экземпляра, объявленного как константа (let), вызовет ошибку компиляции.
  3. Внутри mutating метода вы можете присвоить полностью новый экземпляр свойству self.

Ответ 18+ 🔞

Слушай, давай разберём эту штуку с mutating, а то народ иногда путается, как будто это какая-то магия, блядь.

Вот представь себе: структура (struct) или перечисление (enum) — это как бы твоя личная ксерокопия документа. По умолчанию, когда ты её кому-то передаёшь в метод, ты говоришь: «На, посмотри, но не рисуй на ней, сука, ручкой!» Это для безопасности, чтобы никто твои исходные данные не испоганил случайно.

А mutating — это когда ты такой: «Знаешь что? Вот тебе ручка, можешь прямо тут исправить опечатку, я разрешаю». То есть ты явно даёшь добро на изменение свойств самой этой структуры изнутри её же метода.

Зачем это, спрашивается, нужно? Да затем, что внутри обычного метода self — это константа (let). Попробуй её изменить — компилятор тебе мозги выест: «Ты чё, охренел? Я тебе let дал, а ты в него пишешь!». А mutating как раз и говорит компилятору: «Расслабься, братан, я тут сам всё контролирую, дай мне изменяемый доступ».

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

struct Координата {
    var x = 0.0, y = 0.0

    // Без mutating тут была бы ошибка! А так — всё легально.
    mutating func подвиньНа(x deltaX: Double, y deltaY: Double) {
        x += deltaX // Меняем своё же свойство
        y += deltaY
        // Вообще внутри можно и self целиком переписать, если захочется!
        // self = Координата(x: 5, y: 5)
    }
}

var мояТочка = Координата(x: 1.0, y: 1.0)
мояТочка.подвиньНа(x: 2.0, y: 3.0) // Всё работает, потому что мояТочка — `var`
print(мояТочка) // Координата(x: 3.0, y: 4.0)

let замороженнаяТочка = Координата(x: 1.0, y: 1.0)
// замороженнаяТочка.подвиньНа(x: 2.0, y: 3.0) // А вот тут, блядь, будет пиздец и ошибка компиляции! Ты её объявил как константу (`let`), а лезешь с mutating-методом. Нельзя так.

Важные нюансы, чтобы не облажаться:

  1. Для классов (class) это говно не нужно. Классы — reference-типы, они всегда передаются по ссылке. Там внутри методов и так можно всё менять без всяких mutating. Другое дело, ёпта.
  2. mutating и let — враги на хуй. Вызвать изменяющий метод у константной структуры — всё равно что попытаться изменить надпись на застывшем бетоне. Не выйдет, только компилятор обругает.
  3. Внутри mutating метода можно вообще подменить себя полностью, присвоив новый экземпляр в self. Это как сказать: «Всё, старая версия себя меня не устраивает, вот теперь я вот этот новый, красивый объект». Мощная штука, кстати.

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