Всегда ли при присваивании структуры в Swift создается полная физическая копия?

Ответ

Нет, не всегда. Swift стремится избегать излишних копий для повышения эффективности, сохраняя при этом гарантии value-семантики.

Что происходит на самом деле:

  1. Логическое копирование происходит сразу. Две переменные становятся независимыми с точки зрения языка.
  2. Физическое копирование данных откладывается до первой модификации одной из копий благодаря Copy-on-Write (CoW).

Пример с CoW:

struct LargeStruct { var data: [Int] = Array(1...1000) }

var a = LargeStruct()
var b = a // Логическая копия. Физически `a.data` и `b.data`
          // могут указывать на один массив в памяти (нет копирования 1000 элементов).

b.data.append(1001) // ТЕПЕРЬ, при модификации `b`, происходит реальное
                    // копирование внутреннего массива (`data`).

Оптимизации компилятора: Для простых, небольших структур (например, Point с двумя Int) компилятор может пропустить даже логическую отложенность и выполнить копирование сразу, так как это дешево.

Вывод: Swift гарантирует семантику копирования по значению (изменение одной копии не влияет на другую), но реализация использует оптимизации (CoW), чтобы физическое копирование происходило только при необходимости.

Ответ 18+ 🔞

А, слушай, тут народ часто путается, как эти ваши структуры в Свифте копируются. Думают, что каждый раз, когда присваиваешь, начинается пиздец — весь массив по байтику перетаскивается. Ну, блядь, не всегда же!

Что на самом деле творится, ёпта:

  1. Копия по документам создаётся мгновенно. Для языка ты получил две независимые сущности, и хуй с ним.
  2. А вот реальное копирование данных в памяти откладывается до последнего, пока ты одну из копий не начнёшь трогать. Это и есть та самая хитрая жопа под названием Copy-on-Write (CoW).

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

struct LargeStruct { var data: [Int] = Array(1...1000) }

var a = LargeStruct()
var b = a // Вот тут, блядь, логически скопировали. А физически `a.data` и `b.data`
          // могут спокойно тыкать в один и тот же массив в памяти. Никаких 1000 элементов не копируется, овердохуища экономия!

b.data.append(1001) // А ВОТ ТУТ, сука, когда ты полез в `b` и начал менять, — вот тогда-то и происходит реальное,
                    // полномасштабное копирование внутреннего массива (`data`). Чих-пых тебя в сраку, оптимизация!

Ещё один фокус: Для простеньких структур, вроде точки с парой Int, компилятор может вообще не париться с этой отложенностью и скопировать всё сразу — ибо дешево, блядь.

Короче, вывод: Свифт гарантирует тебе поведение как при копировании по значению — потрогал одну копию, другая не дернулась. Но под капотом он не мудак, и использует оптимизации (ту самую CoW), чтобы реально копировать память только тогда, когда без этого уже совсем никак, нахуй.