Как mutating метод изменяет экземпляр структуры в Swift?

Ответ

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

Пример:

struct Point {
    var x: Int
    var y: Int

    // mutating метод изменяет свойства
    mutating func moveBy(x deltaX: Int, y deltaY: Int) {
        x += deltaX
        y += deltaY
    }
}

var point = Point(x: 0, y: 0)
point.moveBy(x: 5, y: 5) // point теперь Point(x: 5, y: 5)

Ключевые аспекты:

  • Только для var: Вызов mutating метода для экземпляра, объявленного через let, вызовет ошибку компиляции.
  • Семантика value-типа: Изменение происходит через подмену всего экземпляра, что соответствует поведению value-типов.
  • Оптимизация (Copy-on-Write): Для сложных структур (например, Array) Swift использует механизм Copy-on-Write, чтобы избежать излишнего копирования, пока данные не будут реально изменены.

Ответ 18+ 🔞

Слушай, вот объясняю тебе как для дебила, но ты не обижайся, а то я сам от этого mutating чуть не поседел. Представь, что структура — это как твой паспорт, бумажная ксерокопия. Настоящий, блядь, value-тип.

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

struct Point {
    var x: Int
    var y: Int

    // Смотри, вот этот метод — он как раз таки мутирует, то есть делает новую копию
    mutating func moveBy(x deltaX: Int, y deltaY: Int) {
        x += deltaX // Тут меняем
        y += deltaY // И тут меняем
    }
}

var point = Point(x: 0, y: 0) // Объявил через `var`, потому что с `let` нихуя не выйдет
point.moveBy(x: 5, y: 5) // Вжух! point теперь Point(x: 5, y: 5)

А теперь главные моменты, чтобы ты не облажался:

  1. Только для var, ёбаный насос! Если твой экземпляр объявлен через let — всё, пиши пропало. Компилятор тебе такую ошибку влепит, что мало не покажется. let — это константа, её менять нельзя, даже через mutating. Это как прибить паспорт гвоздём к столу — хоть ты новый и нарисуй, оторвать-то старый нельзя.

  2. Семантика, блядь, value-типа. Всё честно. Мы не лезем внутрь и не ковыряемся в памяти. Мы делаем новую хуйню и подменяем старую. Чисто, прозрачно, без подвохов.

  3. А вот тут магия (Copy-on-Write). Чтобы не копировать гигабайты данных просто так, Swift включает режим хитрожопости. Возьмём, например, массив (Array). Пока ты его не начал менять — все переменные могут тихо ссылаться на одни и те же данные в памяти. Но как только ты через mutating метод попытаешься что-то там пошатать — только тогда Swift сгоряча создаст отдельную, настоящую копию данных. Оптимизация, блять, чтобы не тратить ресурсы зря. Умно, да?