Structs have value semantics. When you pass them around/assign them to things, they get copied, and unless it is var, you can't mutate them.
Return values are not vars, you cannot change a return value. You can put it in a var variable first, and then append to pets
var person = self.people.findPerson(by: id)
person.pets.append(contentsOf: newPets)
This makes the compiler error go away, but person here is a copy of the return value, so your modification here doesn't actually change the things in the self.people array. self.people is declared a let constant in the first place, so you better change that too:
var people = [Person]()
One way is to have findPerson return the index of the person found:
extension Array where Element == Person {
func findPerson(by id: UUID) -> Int {
let foundPerson = self.firstIndex { person in
person.id == id
}
return foundPerson!
}
}
This way, you can do:
let index = self.people.findPerson(by: id)
self.people[index].pets.append(contentsOf: newPets)
Note how I am doing things directly on self.people, rather than through another variable. addPetFor also needs to be marked mutating.
Another way is to have findPerson take a closure, and you write in that closure what you want to do with the found person:
extension Array where Element == Person {
mutating func findPerson(by id: UUID, andDo block: (inout Person) -> Void) -> Int {
let index = self.firstIndex { person in
person.id == id
}
block(&self[index!])
}
}
Note how findPerson is now marked mutating, because it now mutates the array (self). self[index] is passed by inout, which is kind of like "by reference".
Then you can do:
self.people.findPerson(by: id) {
$0.pets.append(contentsOf: newPets)
}