1

I know I'm doing something wrong but I do not know what..

All I would like to do here is add some pet to my Array<Pet>

What am I doing wrong here?

The error is

Cannot use mutating member on immutable value: function call returns immutable value

import UIKit

struct Person {
    let id = UUID()
    let name: String
    var pets: [String]
}

final class City {
    let people = [Person]()
    
    func addPetFor(id: UUID, newPets: [String]) {
        self.people.findPerson(by: id).pets.append(contentsOf: newPets) //<-- error Cannot use mutating member on immutable value: function call returns immutable value
    }
}

extension Array where Element == Person {
    func findPerson(by id: UUID) -> Person {
        let foundPerson = self.first { person in
            person.id == id
        }
        return foundPerson!
    }
}

Picture of failed attempt to append an array to an array

2 Answers 2

2

As the error tells you, your findPerson method is returning an immutable struct, so you can't edit it.

There are a few solutions to this -- two are included in the code sample below:

final class City {
    var people = [Person]()
    
    func addPetFor(id: UUID, newPets: [String]) {
        guard let index = self.people.firstIndex(where: { $0.id == id }) else {
            assertionFailure("Not found")
            return
        }
        self.people[index].pets.append(contentsOf: newPets)
    }
    
    func addPetFor2(id: UUID, newPets: [String]) {
        self.people = self.people.map {
            if $0.id == id {
                var personCopy = $0
                personCopy.pets.append(contentsOf: newPets)
                return personCopy
            } else {
                return $0
            }
        }
    }
}

In both cases, the code has a reference to where the Person belongs in the people array, so it can modify it. Note that people also has to be var, not let, for the same reason -- it needs to be mutable.

Sign up to request clarification or add additional context in comments.

Comments

2

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)
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.