Swift struct mutating a variable not working? [duplicate]

纵饮孤独 提交于 2019-12-04 20:03:04

Creating a mutating function on a struct doesn't change the value semantics of structs. As soon as a struct instance is mutated, a copy is made.

You say:

if var lastItem = filteredItems.last {
    print("Will start \(lastItem.modelNo)")
    lastItem.startCar(status: true)
}

So, lastItem contains an instance of a car you are going to start. Under the covers this is the same instance of the car that is in filteredItems, which is the same instance that is in arrCars. But, as soon as you mutate the Car in lastItem, a copy is made, so lastItem no longer has the same instance as the one in arrCars. filteredItems and arrCars still contain the original, unaltered Car instance.

You can change Car to be a class rather than a struct so that it has reference semantics to get the behaviour you want.

The other option is to modify the instance in place; something like arrCars[0].startCar(status: true). To do this you will need to get an array containing the indicies of the cars you want to start rather than the cars themselves:

private func filtered() {
    for item in arrCars {
        let matchingIndices = arrCars.enumerated().compactMap { (index,car) in
            return car.id == item.id ? index:nil
        }
        if matchingIndices.count > 0 {
            if let lastIndex = matchingIndices.last {
                print("Will start \(arrCars[lastIndex].modelNo)")
                arrCars[lastIndex].startCar(status: true)
            }
        }
    }

    let cars = arrCars.filter { (car) -> Bool in
        car.start == true
    }

    print(cars.count)
}

However, where a model object requires mutability or is stateful, a struct is probably not suitable.

The answers you've gotten so far are not wrong, but they seem a little confusing. Let's look at it a little more simply. The problem is this line:

if var lastItem = filteredItems.last

This yields a Car as lastItem. But it is not the same Car as the one in the filteredItems.

That is because a struct is a value class. Assignment copies the struct. Thus, what you do to lastItem has no effect on what is sitting inside filteredItems.

So, you are not having any trouble mutating a struct. You are mutating lastItem just fine! The problem is that the lastItem Car is not the same object as any Car inside filteredItems.

Firstly try to run this code and check if it solves the problem if we stick to Structures. Then I'll try to explain why this helps:

struct Car {
    var id = 0
    var start = false
    var modelNo: String

    init(start: Bool, id: Int, model: String) {
        self.start = start
        self.id = id
        self.modelNo = model
    }

    mutating func startCar(status: Bool) {
        start = status
    }
}

var arrCars: Array<Car> = [Car]()

for i in 1...10 {
    let md = Car(start: false, id: i <= 5 ? 0 : 1, model: "Model\(i)")
    arrCars.append(md)
}

private func filtered() {
    for item in arrCars {
        let filteredItems = arrCars.filter { $0.id == item.id }
        if filteredItems.count > 0 {
            // solves the problem
            arrCars[filteredItems.count-1].startCar(status: true)
            print("Will start \(arrCars[filteredItems.count-1].modelNo)")
        }
    }

    let cars = arrCars.filter { (car) -> Bool in
        car.start == true
    }

    print(cars.count)
}

filtered()
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!