I am trying to modify struct element in array. I found that you can do that by accessing(iterate) the struct by index, but you can't if you use 'for in' loop or 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
}
Can someone explain?
No, forEach does not mutate the original array. You can achieve what you are looking for by giving a second parameter of index , then updating the values of the original array.
To sum up: Using a for loop gives us a much greater degree of control over an iteration, while using forEach enables us to take advantage of the power of closures and first class functions, even though we won't be able to stop an iteration once it was started (apart from throwing an error, that is).
C = struct2cell( S ) converts a structure into a cell array. The cell array C contains values copied from the fields of S . The struct2cell function does not return field names. To return the field names in a cell array, use the fieldnames function.
What can a mutating function do? Essentially, a function that's been marked as mutating can change any property within its enclosing value. The word “value” is really key here, since Swift's concept of structured mutations only applies to value types, not to reference types like classes and actors.
As stated in other answers you can't mutate in a for-in
loop or in a .forEach
method.
You can either use your last formulation which is short and concise:
for index in personArray.indices {
personArray[index].age = 10
}
Or mutate the orignal personArray
entirely:
personArray = personArray.map { person in
var person = person // parameter masking to allow local mutation
person.age = 10
return person
}
Note that the second option may seems less efficient as it creates a new instance of Person
each time but Swift seems to be well optimised for those cases.
Here is a bonus if you really want a mutating counterpart for .forEach
method :
extension MutableCollection {
mutating func mutateEach(_ body: (inout Element) throws -> Void) rethrows {
for index in self.indices {
try body(&self[index])
}
}
}
This is a wrapper around array mutation equivalent to your first formulation, you can use it like intended:
personArray.mutateEach { $0.age = 10 }
In Swift a struct is a value type. In the for or foreach loop the person item is a value, and if it were mutable you would only be changing a copy of the original, not the original as you intend.
If you really want an updatable reference to the struct inside the loop add a var keyword, but remember you are updating a copy not the original.
for var person in personArray {
person.age = 10 // updates a copy, not the original
}
By contrast class is a reference type and in the loop each item is a reference to the original. Updating this reference value now updates the original.
Change your definition of Person to class instead of struct and it will work as expected. For a complete explanation see https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With