struct Vector<SOType: SerializableObject> : RangeReplaceableCollection {
... // all the code you would expect to do the conformance
}
when I realized that in some cases, I wanted to hand back a collection which needed to be treated as read-only. Since there's quite a bit of code to write in making a type conform to RangeReplaceableCollection, I thought, no problem, I'll use inheritance on structures and first write
struct ImmutableVector<SOType: SerializableObject> : Collection {
// implements everything you need for a collection
}
struct Vector<SOType: SerializableObject> : ImmutableVector<SOType>, RangeReplaceableCollection {
// adds in just those few extra things you need to be able to modify the collection
}
I figured that inheritance would stop me from having to duplicate a lot of code. Then, of course, I realized you can't inherit structs.
So what's the sensible way to implement both a mutable and an immutable version of a collection with lots of specialized behavior with as little duplication as possible? I.e. both the mutable and immutable version require an Iterator class, etc. I don't want to replicate that. I don't want to have to write all my "startIndex" and "endIndex" etc. definitions. I feel like I should be able to just add a few more lines of code to piggy-back the mutable collection off the immutable one, but I'm just missing how to get it.
Since structs are value types, you shouldn't need mutable and immutable variants. When you mutate one, it creates a new value, always.
If your struct is backed by referencey storage, you'll need to do a "copy on write" to avoid mutating storage that's backing another value of the same struct. Swift has an extra feature to make copy on write more efficient: isKnownUniquelyReferenced. Here's a quick blog post about it (under an older name, isUniquelyReferencedNonObjC): Struct Semantics in Swift — Chris Eidhof
Sorry, you misunderstand. They may be structs, but when you copy them, they’re still pointing to the same underlying storage.
Think of the struct as a really lightweight proxy, so in essence, this has class semantics. (And I would have declared it as a “class” but then the protocol gives you mutating class functions, which is unpleasant.)
The issue is that I want to hand back an API to a person which doesn’t allow change at all through this vector. Essentially, I’m giving the user a read-only view of the underlying collection.
There’s a separate, more fine-grained and heavily error-laden API when you want to mutate it.
mutable collections in swift don’t let you throw from the subscript accessor, so i don’t want to let the user mutate the collection, (where an error can occur for any operation) using the mutable collection API.
So, you want this to have reference semantics (when mutable). Okay.
In that case, you can possibly get some abstraction with a protocol that both conform to, but yeah, Swift doesn't have struct inheritance, mainly to avoid the "slicing" problem that's well-known in C++. (In theory a language could support some kind of "inheritance without subtyping", but at some point the added complexity doesn't pay for itself.)