Swift 4.2) Мутировать массив структуры с for_in/forEach по сравнению с доступом по индексу

Я пытаюсь изменить элемент структуры в массиве. Я обнаружил, что вы можете сделать это, обратившись (итерировать) к структуре по индексу, но вы не можете, если используете цикл for in или forEach{}.

struct Person
{
  var age = 0
  var name = "James"
}

var personArray = [Person]()
personArray += [Person(), Person(), Person()]


personArray.forEach({$0.age = 10}) // error: "Cannot assign to property: '$0' is immutable"

for person in personArray { 
  person.age = 10 // error: "Cannot assign to property: 'person' is a 'let' constant"
}


for index in personArray.indices {
  personArray[index].age = 10 // Ok
}

Может кто-нибудь объяснить?


person juliannehong    schedule 11.10.2018    source источник
comment
потому что это массив структуры, а не класса Класс передается по ссылке, а структура передается по значению   -  person Magdy Zamel    schedule 12.10.2018


Ответы (2)


Как указано в других ответах, вы не можете мутировать в цикле for-in или в методе .forEach.

Вы можете использовать последнюю формулировку, которая будет короткой и лаконичной:

for index in personArray.indices {
    personArray[index].age = 10
}

Или полностью изменить исходный personArray:

personArray = personArray.map { person in 
    var person = person // parameter masking to allow local mutation
    person.age = 10
    return person
}

Обратите внимание, что второй вариант может показаться менее эффективным, поскольку он каждый раз создает новый экземпляр Person, но Swift, похоже, хорошо оптимизирован для таких случаев. Профилировщик времени сообщил о том, что второй вариант с массивом из 1 000 000 Persons работает почти в 2 раза быстрее.

Вот бонус, если вам действительно нужен мутирующий аналог для метода .forEach, используйте расширение для MutableCollection:

extension MutableCollection {
    mutating func mutateEach(_ body: (inout Element) throws -> Void) rethrows {
        for index in self.indices {
            try body(&self[index])
        }
    }
}

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

personArray.mutateEach { $0.age = 10 }
person Louis Lac    schedule 17.01.2020

В Swift структура представляет собой тип value. В цикле for или foreach элемент person является значением, и если бы он был изменяемым, вы бы изменили только копию оригинала, а не оригинал, как вы намеревались.

Если вам действительно нужна обновляемая ссылка на struct внутри цикла, добавьте ключевое слово var, но помните, что вы обновляете копию, а не оригинал.

for var person in personArray {
    person.age = 10 // updates a copy, not the original
}

Напротив, класс является ссылочным типом, и в цикле каждый элемент является ссылкой на оригинал. Обновление этого эталонного значения теперь обновляет оригинал.

Измените свое определение Person на class вместо struct, и оно будет работать так, как ожидалось. Полное объяснение см. на странице https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html

person Dale    schedule 12.10.2018