Is it safe mutate collection through its indices on fly?

Hello. Often I need update each element in collections. And I wrote next function

public protocol Adjust {
    mutating func preAnimationAdjust()
}

func adjustEachElementInCollection<C: MutableCollection>(c: inout C) where C.Element: Adjust {
    for index in c.indices {
        var item = c[index]
        item.preAnimationAdjust()
        c[index] = item
    }
}

And next test gives away a correct result

    struct Bee: PreAnimationAdjust {        
        var name: String
        mutating func preAnimationAdjust() {
            name += "A"
        }
     }
    

    public func test() {
        var bees = [Bee(name: "Anna"), Bee(name: "Anton"), Bee(name: "Kit")]
        print(bees) // [Bee(name: "Anna"), Bee(name: "Anton"), Bee(name: "Kit")]
        adjustEachElementInCollection(c: &bees)
        print(bees) //[Bee(name: "AnnaA"), Bee(name: "AntonA"), Bee(name: "KitA")]
    }

Is it safe approach to mutation a collection? In final I need same type of a collection after mutation, therefore the .map does not fit.

This is not quite right. It will work, but it can run into a performance cliff if the collection’s Indices type retains the collection itself (as, for example, Dictionary.Values.Indices does).

In general, you should not use the indices property for mutation. Instead, manually advance an index. This is mentioned in the documentation of indices.

Here’s a method that lets you write in-place transformations of a collection:

extension MutableCollection {
  mutating func transform(_ transform: (inout Element) throws -> Void) rethrows {
    var i = startIndex
    while i != endIndex {
      try transform(&self[i])
      formIndex(after: &i)
    }
  }
}

Usage:

myNumbers.transform{ $0 += 1 }
2 Likes

Wow, it is very smart closure usage

Terms of Service

Privacy Policy

Cookie Policy