In-place map for MutableCollection

This implementation falls into a performance trap, as described in the documentation for indices. Essentially, indices might hold a reference to self, in which case mutating self while iterating over indices creates an extra copy of self. In the standard library, this occurs for Dictionary.Values.

The recommended approach is to manually advance an index.

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

Also, if you want to directly pass in a function (eg. sin) rather than a closure, then the parameter should not be inout:

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

The difference is in how you call it:

x.mutateAll{ $0 *= $0 }    // inout
x.mutateAll{ $0 * $0 }     // non-inout
x.mutateAll(sin)           // non-inout
11 Likes