7

Here's an obvious situation that must arise all the time for people:

struct Foundation {
    var columns : [Column] = [Column(), Column()]
}
struct Column : CustomStringConvertible {
    var cards = [Card]()
    var description : String {
        return String(describing:self.cards)
    }
}
struct Card {}
var f = Foundation()
for var c in f.columns {
    c.cards.append(Card())
}

That code is legal but of course it has no effect on f, because var c is still a copy — the actual columns of f are unaffected.

I am not having any difficulties understanding why that happens. My question is what people generally do about it.

Clearly I can just make the whole matter go away by declaring Column a class instead of a struct, but is that what people usually do? (I'm trying to follow a mental stricture that one should avoid classes when there's no need for dynamic dispatch / polymorphism / subclassing; maybe I'm carrying that too far, or maybe there's something else people usually do, like using inout somehow.)

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • 3
    Adding a "mutating each element" method has been discussed on the Swift forums (https://forums.swift.org/t/idea-mutatingforeach-for-collections/18442 & https://forums.swift.org/t/in-place-map-for-mutablecollection/11111). However I believe the eventual goal is to implement this at language level once we have a proper ownership model, [allowing you to say something like](https://github.com/apple/swift/blob/decff7c700005a8af526736cc42f3960654998ae/docs/OwnershipManifesto.md#mutating-iteration) `for inout c in f.columns`. – Hamish Jan 02 '19 at 23:27
  • 3
    I don't know what people generally do. What I have seen as “workaround” is to iterate over the indices instead: `for i in f.columns.indices { f.columns[i].cards.append(Card()) }` – Martin R Jan 02 '19 at 23:35
  • @MartinR That's quite a good compromise, would you like to give that as an answer? – matt Jan 03 '19 at 00:04
  • @Hamish Hilarious because of course I tried typing `for inout` even though I knew it wouldn't compile. :) – matt Jan 03 '19 at 00:04

1 Answers1

2

As of Swift 4, a compromise is to iterate over the indices of a mutable collection instead of the elements themselves, so that

for elem in mutableCollection {
    // `elem` is immutable ...
}

or

for var elem in mutableCollection {
   // `elem` is mutable, but a _copy_ of the collection element ...
}

becomes

for idx in mutableCollection.indices {
    // mutate `mutableCollection[idx]` ...
}

In your example:

for idx in f.columns.indices {
   f.columns[idx].cards.append(Card()) 
}

As @Hamish pointed out in the comments, a future version of Swift may implement a mutating iteration, making

for inout elem in mutableCollection {
   // mutate `elem` ...
}

possible.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382