I had written this:
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.
Suggestions?
jrose
(Jordan Rose)
2
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.
Does that make sense?
jrose
(Jordan Rose)
4
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.)
At some point we may get something like [Proposal Draft] automatic protocol forwarding, which (I think) would help a lot for cases like this.