Случайно мутировав копию структуры вместо самой структуры - PullRequest
0 голосов
/ 02 декабря 2018

Программируя с типами идентичности в течение многих лет, я нахожу мутантные типы значений очень напряженными для использования из-за постоянного риска случайного присвоения (и, следовательно, копирования) новой переменной, а затем мутирования этой копии и ожиданиячтобы увидеть эти изменения, отраженные в исходной структуре (пример приведен в конце).

Мой актуальный вопрос : Какие существуют подходы / стили / практики кодирования для предотвращения такого родачто происходит?


Что я думаю : я не могу себе представить ответ на этот вопрос просто "просто помните, что вы имеете дело сstruct, а не class ", потому что это чрезвычайно подвержено ошибкам, особенно потому, что class-ness / struct-ness совершенно непрозрачны для кода, использующего любую конкретную структуру данных.

Я также вижу много разговоров об использовании" чисто функциональный стиль", за который я все, кроме:

  • , кажется очень неловким в тех случаях, когда метод мутации также необходимвернуть значение (как в моем примере ниже).Конечно, я мог бы вернуть кортеж типа (newStructInstance, returnValue), но это не может быть The Way.
  • Я представляю, что создание новых копий структур, когда вам нужны мутации, не может быть эффективным (в отличие от переназначенияструктуры, которые, насколько я понимаю, создают новый экземпляр без каких-либо затрат).

Некоторые возможные ответы на мой вопрос могут быть:

  • Ваш примернадуманный, и такого рода вещи редко, если вообще когда-либо случаются, на практике
  • вы используете шаблон итератора (анти) (см. код ниже), так что это ваш первоначальный грех прямо здесь
  • облегчениедо mutating и просто используйте struct s для неизменяемых типов и class es для изменяемых типов

Есть ли что-нибудь в правильном направлении?


Пример : Скажем, у нас есть структура, которая просто оборачивает целое число, которое увеличивается на 1 с помощью метода мутации next().

struct Counter {
    var i: Int = 0

    mutating func next() -> {
        self.i += 1
        return self.i
    }
}

В каком-то инициализаторе где-то мы создаем экземпляр, подобный этому

self.propertyWithLongAndAnnoyingName = Counter()

aПозже в той же области, забыв, что это свойство содержит структуру, мы присваиваем его кратко названной локальной переменной p и увеличиваем , что .

var p = self.propertyWithLongAndAnnoyingName
d.next() // actually increments a copy

1 Ответ

0 голосов
/ 02 декабря 2018

Ваш вопрос, если это не просто сложный тролль, кажется скорее психологическим, чем практическим, и, как таковой, вероятно, не очень подходит для переполнения стека: это вопрос мнения.Однако, принимая вас всерьез и полагая, что вопрос искренний, я могу вам на опыте сказать, что как человек, который годами программировал в Objective-C, а затем изучал Swift, когда он был впервые выпущен для публики, о существованииОбъекты типа значения, такие как структуры, представляют собой огромный рельеф , а не проблему, как вы, похоже, полагаете.

В Swift структура - это тип объекта par excellence .Когда вам просто нужен объект, вы используете структуру.Использование классов является довольно исключительным, ограничиваясь такими случаями, как:

  • Вы используете Какао (написано в Objective-C, и вы просто должны признать, что его объекты являются классами)

  • Вам необходимо отношение суперкласс / подкласс (очень редко в чистом программировании на Swift; обычно это происходит только потому, что вы используете Какао)

  • цель состоит в том, чтобы представить внешнюю реальность, где индивидуальная идентичность является критической (например, UIView должен был бы быть ссылочным типом, а не типом значения, потому что в интерфейсе действительно есть отдельные представления, и мы должны иметь возможность различать их)

  • Истинная изменчивость на месте - это желаемое

В общем, поэтому большинство программистов чувствуют противоположность душевного состояния васГипотеза: они удивляются, когда объект неожиданно оказывается ссылочным типом (классом) и мутация влияет на удаленную ссылку.В приведенном вами примере кода:

var p = self.propertyWithLongAndAnnoyingName
p.next()

... было бы полным шоком, если бы self.propertyWithLongAndAnnoyingName также был мутирован.Типы значений представляют собой форму безопасность .Присвоение p и мутация p будут точно на изолировать self.propertyWithLongAndAnnoyingName от изменения.

Фактически, само Какао идет на все, чтобы охранять против такого рода вещей.Вот почему, например, шаблон состоит в том, что propertyWithLongAndAnnoyingName никогда не будет (как видно снаружи) NSMutableString - это будет NSString, которая создает забор неизменности вокруг объекта, даже если он является ссылочным типом,Таким образом, использование структур в Swifts решает проблему, которую какао приходится решать с помощью гораздо более сложных и (для многих начинающих) загадочных мер.

Однако подобные случаи в действительности относительно редки.факт, потому что другой аспект вашей гипотезы также ложен:

Я не могу себе представить ответ на этот вопрос, просто «просто помните, что вы имеете дело со структурой, а не с классом»,потому что это чрезвычайно подвержено ошибкам, особенно потому, что класс-ность / структура-абсолютно непрозрачна для кода, использующего любую конкретную структуру данных знаю, имею ли я дело со структурой или классом.Проще говоря, если это объект библиотеки Swift (String, Array, Int и т. Д.), Это структура.(Библиотека вообще не определяет классы.) Если это объект Какао (UIView, UIViewController), то это класс.

Действительно, соглашение об именах обычно помогает вам.Наложение Foundation является тому примером.NSData - это класс;Данные - это структура.

Также обратите внимание, что вы не можете поменять ссылку let на структуру, но вы можете мутировать ссылку let на класс.Таким образом, на практике вы очень быстро испытываете что есть, потому что вы всегда используете let, если можете.

Наконец, если вам действительно важно передать объект какой-либо третьей стороне для мутации на месте, то для этого inout.И в этом случае вы это знаете, потому что вам нужно явно передать ссылку (адрес).Если параметр, который вы передаете, равен , а не inout, вы будете считать, что ваш исходный объект защищен от мутаций, и вы будете этому рады.

...