0

If I loop through an array of Class objects I can make changes to a property on it

class Country {
    var name: String?
    var region: String?

    init(name: String?, region: String?) {
        self.name = name
        self.region = region
    }
}

let canada = Country(name: "Canada", region: "North America")
let mexico = Country(name: "Mexico", region: "North Ameria")
let france = Country(name: "France", region: "Europe")
let korea = Country(name: "Korea", region: "Asia")

var countryArr = [canada, mexico, france, korea]

// this works fine
let transformed = countryArr.map { $0.name = "Random" }

But if I try this with Struct objects I get

Cannot assign to property: '$0' is immutable

struct Country {
    var name: String?
    var region: String?
}

var canada = Country(name: "Canada", region: "North America")
var mexico = Country(name: "Mexico", region: "North Ameria")
var france = Country(name: "France", region: "Europe")
var korea = Country(name: "Korea", region: "Asia")

var countryArr = [canada, mexico, france, korea]

// this gets an error
let transformed = countryArr.map { $0.name = "Random" }
0

1 Answer 1

0

The issue is caused by the fact that structs are value types, so mutating any properties of the struct mutates the struct instance itself as well and the closure input arguments in map are immutable. So when you try to mutate a property $0 in the closure of map, you are trying to mutate $0 itself in case map is called on a collection of value types.

On the other hand, classes are reference types, so mutating a property of a class instance doesn't mutate the instance itself.

A solution for your problem is to create a mutable copy of the struct instance in the map, mutate its name property and return that. There are two solutions, if you have a small number of properties on your type, calling its memberwise initialiser is easier, but if you have a lot of properties and only want to mutate a few, copying the struct and then modifying the necessary properties is the better choice.

let transformed = countryArr.map { Country(name: "Random", region: $0.region) }
let transformed2 = countryArr.map { country->Country in
    var copy = country
    copy.name = "Random"
    return copy
}
Sign up to request clarification or add additional context in comments.

8 Comments

Hey thanks for the explanation. I can’t upvote for 2 minutes so wait a few.
let transformed = countryArr.indices.map { countryArr[$0].name = "Random" } also should work
@E.Coms if countryArr is mutable, it does work, but the other two solutions work even if the array itself is immutable. The first solution even works if the properties themselves are immutable.
@DávidPásztor So using .map {$0 } on a struct will actually attempt to change the memory address of the original struct or will it try to create a new struct at a new memory address?
@LanceSamaria your example wouldn't actually compile, since as I've already stated in my answer, the closure input argument is immutable. As I've also already stated, structs are value types, so changing a struct property does change the struct instance itself as well
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.