Search code examples
swiftcontainersmutable

Is the mutability of Swift containers shallow? Or can an element be mutated in place?


I have a collection of objects in a Set. The objects' type follows GeneratorType, so I have a mutating method next. Although I can mutate the set by adding/removing elements, I don't know how to mutate an element. Using both for-in statement and the forEach method give errors about the element being immutable. Is the mutability of Swift containers shallow?

Is there a way to call a mutating method on a contained element? Does it work for other collection/sequence types besides Set? (My changes would change each object's hash, and maybe I'm not allowed to affect a set like that.)


Solution

  • The situation is similar to that of mutating one of a dictionary's values, where that value is a value type. You can't do it, even if your reference to the dictionary is a var reference. Typically, you'll just pull the value out, mutate it, and put it back again:

    var d = ["key":"hell"]
    var val = d["key"]!
    val.append(Character("o"))
    d["key"] = val
    

    Your Set works similarly.

    var s = Set(["hell"])
    var val = s.remove("hell")!
    val.append(Character("o"))
    s.insert(val)
    

    There are other ways to notate that, but in effect they amount to the same thing.

    The thing to keep in mind here is that no value type, e.g. a struct, is truly mutable. We speak of it as if it were, and so does Swift with its mutating functions, but mutation actually consists of assigning a new value back into the reference. That is why the reference must be a var. There is thus no such thing as mutation in place for any value type.

    Now, of course, with a reference type, e.g. a class, the situation is completely different:

    let s = Set([NSMutableString(string:"hell")])
    s.first!.appendString("o")
    s // {"hello"}