Почему в методах классов Swift не нужен модификатор `mutating`?

Ответ

Ключевое слово mutating требуется только для value-типов (структур и перечислений), но не для ссылочных типов (классов). Это следствие фундаментального различия в их семантике.

Причина:

  • Классы (ссылочные типы): Передаются по ссылке. Метод, изменяющий свойство экземпляра, работает с исходным объектом в памяти. Изменение «на месте» не требует создания новой копии.
  • Структуры (value-типы): Передаются по значению (копируются). Ключевое слово mutating явно указывает компилятору, что метод собирается изменить уникальную копию self, и для этого необходимо сделать текущий экземпляр изменяемым (var).

Пример сравнения:

// КЛАСС - mutating не нужен
class PersonClass {
    var name: String
    init(name: String) { self.name = name }
    func changeName(to newName: String) {
        name = newName // Изменяет исходный объект
    }
}

// СТРУКТУРА - mutating обязателен
struct PersonStruct {
    var name: String
    mutating func changeName(to newName: String) {
        name = newName // Изменяет локальную копию self
    }
}

var person = PersonStruct(name: "Alex")
person.changeName(to: "John") // Работает только если `person` объявлен как `var`

Практическое следствие: Для вызова mutating метода структуры, экземпляр должен быть объявлен с var, а не с let.

Ответ 18+ 🔞

Ну слушай, вот сидишь ты, пишешь на Swift, и тут — бац! — компилятор тебе как даст по рукам: «Мол, мужик, ты чего это в структуре метод без mutating написал, а внутри свойство меняешь?». И начинаешь чесать репу: «А че такое? В классе же всё работало!».

А вся фишка в том, блядь, что классы и структуры — это, простите за выражение, два разных зверя, ёпта. Совершенно разная философия, как говорится.

В чём, сука, соль?

  • Классы (ссылочные типы) — это как твой общий чат в телеге. Ты туда ссылку скинул — все её видят, все по ней переходят. Если кто-то в этом чате (методе) сообщение (свойство) изменит — то оно для всех поменяется, потому что ссылка-то одна. Никакого нового чата не создаётся, всё происходит «на месте». Поэтому им этот самый mutating нахуй не сдался.
  • Структуры (value-типы) — это как бумажные ксерокопии приказа. Каждому — своя копия. Ты свою взял, зачеркнул одну фамилию, вписал другую. Но чтобы это сделать, тебе надо именно свою копию изменить. Слово mutating — это такой крик компилятору: «Э, братан, я щас буду эту самую локальную копию self корежить, приготовься!». И компилятор такой: «Ага, понял. Но раз ты её меняешь, то держи её изначально в переменной (var), а не в константе (let), а то я тебе мозги выебу».

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

// КЛАСС - живёт по понятиям ссылок, мутирует без спроса
class PersonClass {
    var name: String
    init(name: String) { self.name = name }
    func changeName(to newName: String) {
        name = newName // Прямо в оригинале меняем, всем пизда
    }
}

// СТРУКТУРА - ценный тип, требует официального разрешения на изменение
struct PersonStruct {
    var name: String
    mutating func changeName(to newName: String) {
        name = newName // Меняем в своей личной, ебаной копии
    }
}

var person = PersonStruct(name: "Alex")
person.changeName(to: "John") // Сработает, потому что `person` — `var`
// let person = PersonStruct(name: "Alex")
// person.changeName(to: "John") // А вот тут пиздец и ошибка, потому что `let`!

Итог, блядь, простой: Если твой экземпляр структуры заморожен в let, то вызывать mutating методы — это как пытаться изменить надпись на памятнике. Ни хуя не выйдет. Сделай его var, и тогда — пожалуйста, хуярь свои изменения. Вот такая магия, ебать её в сраку.